From f2027d3ee151ff6671a5a0cd860813b65893432e Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Tue, 4 Nov 2025 04:54:09 +0800 Subject: [PATCH 01/52] Emit penetration events on the same frame as penetration --- Nu/Nu/Physics/AetherPhysicsEngine.fs | 29 +++++--- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 96 ++++++++++++++++++-------- Nu/Nu/Physics/PhysicsEngine.fs | 2 +- 3 files changed, 88 insertions(+), 39 deletions(-) diff --git a/Nu/Nu/Physics/AetherPhysicsEngine.fs b/Nu/Nu/Physics/AetherPhysicsEngine.fs index 29e25d76f2..15aa29ac22 100644 --- a/Nu/Nu/Physics/AetherPhysicsEngine.fs +++ b/Nu/Nu/Physics/AetherPhysicsEngine.fs @@ -64,7 +64,9 @@ type private AetherFluidEmitter = { FluidEmitterDescriptor : FluidEmitterDescriptor2d States : AetherFluidParticleState array ActiveIndices : int HashSet - Grid : Dictionary } + Grid : Dictionary + Collisions : FluidCollision ConcurrentBag // OPTIMIZATION: cached to avoid large collections filling up the LOH. + } static let CellCapacityDefault = 20 @@ -446,6 +448,12 @@ type private AetherFluidEmitter = // assert loop completion assert loopResult.IsCompleted + // aggregate concurrent collisions to SArray + let collisionsArray = SArray.zeroCreate fluidEmitter.Collisions.Count + for i in 0 .. dec collisionsArray.Length do // OPTIMIZATION: using TryTake to avoid large array allocation which would happen for IEnumerators on ConcurrentBag + fluidEmitter.Collisions.TryTake &collisionsArray.[i] |> ignore + fluidEmitter.Collisions.Clear () + // relocate particles let outOfBoundsIndices = List 32 fluidEmitter.ActiveIndices.RemoveWhere (fun i -> @@ -454,11 +462,11 @@ type private AetherFluidEmitter = let state = &fluidEmitter.States.[i] state.VelocityUnscaled <- state.VelocityUnscaled + state.Delta state.PositionUnscaled <- state.PositionUnscaled + state.VelocityUnscaled + state.Delta - - ArrayPool.Shared.Return state.PotentialFixtureChildIndexes ArrayPool.Shared.Return state.PotentialFixtures + ArrayPool.Shared.Return state.PotentialFixtureChildIndexes ArrayPool.Shared.Return state.Neighbors + // remove when out of bounds, otherwise update cell let bounds = fluidEmitter.FluidEmitterDescriptor.SimulationBounds let removed = bounds.Contains state.PositionUnscaled = ContainmentType.Disjoint if removed then @@ -467,7 +475,7 @@ type private AetherFluidEmitter = cell.Remove i |> ignore if cell.Count = 0 then fluidEmitter.Grid.Remove state.CellId |> ignore else updateCell i fluidEmitter - removed) |> ignore + removed) |> ignore // aggregate state let particleStates = SArray.zeroCreate fluidEmitter.ActiveIndices.Count @@ -480,20 +488,23 @@ type private AetherFluidEmitter = let outOfBoundsParticles = SArray.zeroCreate outOfBoundsIndices.Count j <- 0 for i in outOfBoundsIndices do - outOfBoundsParticles.[j] <- fromFluid &fluidEmitter.States.[i] + let state = &fluidEmitter.States.[i] + outOfBoundsParticles.[j] <- fromFluid &state + state.Gravity <- Unchecked.defaultof<_> j <- inc j // fin - (particleStates, outOfBoundsParticles, collisions) + (particleStates, outOfBoundsParticles, collisionsArray) // nothing to do - else (SArray.empty, SArray.empty, ConcurrentBag ()) - + else (SArray.empty, SArray.empty, SArray.empty) + static member make descriptor = { FluidEmitterDescriptor = descriptor States = Array.zeroCreate descriptor.ParticlesMax ActiveIndices = HashSet (descriptor.ParticlesMax, HashIdentity.Structural) - Grid = Dictionary HashIdentity.Structural } + Grid = Dictionary HashIdentity.Structural + Collisions = ConcurrentBag () } /// The Aether interface of PhysicsEngineRenderContext. type AetherPhysicsEngineRenderContext = diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index 11677be06f..9bb4c2c569 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -59,7 +59,9 @@ type private Box2dNetFluidEmitter = { FluidEmitterDescriptor : FluidEmitterDescriptor2d States : Box2dNetFluidParticleState array ActiveIndices : int HashSet - Grid : Dictionary } + Grid : Dictionary + Collisions : FluidCollision ConcurrentBag // OPTIMIZATION: cached to avoid large collections filling up the LOH. + } static let CellCapacityDefault = 20 @@ -93,8 +95,8 @@ type private Box2dNetFluidEmitter = static member positionToCellId cellSize (position : Vector2) = v2i (floor (position.X / cellSize) |> int) (floor (position.Y / cellSize) |> int) - static member cellIdToBox cellSize (cellId : Vector2i) = - box2 (cellId.V2 * cellSize) (v2Dup cellSize) + static member cellIdToBox cellSize (cell : Vector2i) = + box2 (cell.V2 * cellSize) (v2Dup cellSize) static member updateDescriptor (descriptor : FluidEmitterDescriptor2d) (fluidEmitter : Box2dNetFluidEmitter) = if not descriptor.Enabled then @@ -307,7 +309,6 @@ type private Box2dNetFluidEmitter = B2Worlds.b2World_OverlapAABB (context, aabb, queryFilter, query, 0n) |> ignore // parallel for 2 - resolve collisions - let collisions = ConcurrentBag () let loopResult = Parallel.ForEach (fluidEmitter.ActiveIndices, fun i -> // NOTE: collision testing must use physics engine units in calculations or the fluid collision in FluidSim page of @@ -444,8 +445,8 @@ type private Box2dNetFluidEmitter = // handle collision response if colliding then - collisions.Add - { FluidCollider = fromFluid &state + fluidEmitter.Collisions.Add + { FluidCollider = fromFluid &fluidEmitter.States.[i] FluidCollidee = B2Shapes.b2Shape_GetUserData shape :?> BodyShapeIndex Nearest = (toPixelV2 nearest).V3 Normal = (toPixelV2Normal normal).V3 } @@ -458,6 +459,12 @@ type private Box2dNetFluidEmitter = // assert loop completion assert loopResult.IsCompleted + // aggregate concurrent collisions to SArray + let collisionsArray = SArray.zeroCreate fluidEmitter.Collisions.Count + for i in 0 .. dec collisionsArray.Length do // OPTIMIZATION: using TryTake to avoid large array allocation which would happen for IEnumerators on ConcurrentBag + fluidEmitter.Collisions.TryTake &collisionsArray.[i] |> ignore + fluidEmitter.Collisions.Clear () + // relocate particles let outOfBoundsIndices = List 32 fluidEmitter.ActiveIndices.RemoveWhere (fun i -> @@ -477,7 +484,6 @@ type private Box2dNetFluidEmitter = let cell = fluidEmitter.Grid.[state.CellId] cell.Remove i |> ignore if cell.Count = 0 then fluidEmitter.Grid.Remove state.CellId |> ignore - state.Gravity <- Unchecked.defaultof<_> else updateCell i fluidEmitter removed) |> ignore @@ -492,20 +498,28 @@ type private Box2dNetFluidEmitter = let outOfBoundsParticles = SArray.zeroCreate outOfBoundsIndices.Count j <- 0 for i in outOfBoundsIndices do - outOfBoundsParticles.[j] <- fromFluid &fluidEmitter.States.[i] + let state = &fluidEmitter.States.[i] + outOfBoundsParticles.[j] <- fromFluid &state + state.Gravity <- Unchecked.defaultof<_> j <- inc j // fin - (particleStates, outOfBoundsParticles, collisions) + (particleStates, outOfBoundsParticles, collisionsArray) // nothing to do - else (SArray.empty, SArray.empty, ConcurrentBag ()) + else (SArray.empty, SArray.empty, SArray.empty) static member make descriptor = { FluidEmitterDescriptor = descriptor States = Array.zeroCreate descriptor.ParticlesMax ActiveIndices = HashSet (descriptor.ParticlesMax, HashIdentity.Structural) - Grid = Dictionary HashIdentity.Structural } + Grid = Dictionary HashIdentity.Structural + Collisions = ConcurrentBag () } + +type private Box2dNetPhysicsEngineContactsTracker = + { NewContacts : ConcurrentDictionary + ExistingContacts : HashSet + } /// The Box2D.NET interface of PhysicsEngineRenderContext. type Box2dNetPhysicsEngineRenderContext = @@ -515,7 +529,7 @@ type Box2dNetPhysicsEngineRenderContext = abstract DrawCircle : position : Vector2 * radius : single * color : Color -> unit /// The Box2D.NET implementation of PhysicsEngine. -and [] Box2dNetPhysicsEngine = +type [] Box2dNetPhysicsEngine = private { mutable PhysicsContextId : B2WorldId Bodies : Dictionary @@ -524,7 +538,9 @@ and [] Box2dNetPhysicsEngine = BreakableJoints : Dictionary CreateBodyJointMessages : Dictionary FluidEmitters : Dictionary - IntegrationMessages : IntegrationMessage List } + IntegrationMessages : IntegrationMessage List // OPTIMIZATION: cached to avoid large arrays filling up the LOH. + ContactsTracker : Box2dNetPhysicsEngineContactsTracker // NOTE: supports thread safety for b2PreSolveFcn. + } member private this.PhysicsContext = B2Worlds.b2GetWorld (int this.PhysicsContextId.index1) @@ -684,8 +700,9 @@ and [] Box2dNetPhysicsEngine = bodyShapeDef.filter.categoryBits <- bodyProperties.CollisionCategories bodyShapeDef.filter.maskBits <- bodyProperties.CollisionMask bodyShapeDef.isSensor <- bodyProperties.Sensor - bodyShapeDef.enableContactEvents <- true // record non-sensor contacts for b2World_GetContactEvents - bodyShapeDef.enableSensorEvents <- true // record sensor contacts for b2World_GetSensorEvents + bodyShapeDef.enablePreSolveEvents <- true // record non-sensor begin contact events via preSolveCallback + bodyShapeDef.enableContactEvents <- true // record non-sensor end contacts via b2World_GetContactEvents + bodyShapeDef.enableSensorEvents <- true // record sensor contacts via b2World_GetSensorEvents bodyShapeDef.userData <- { BodyId = { BodySource = bodySource; BodyIndex = bodyProperties.BodyIndex } BodyShapeIndex = match bodyShapePropertiesOpt with Some p -> p.BodyShapeIndex | None -> 0 } @@ -1401,20 +1418,42 @@ and [] Box2dNetPhysicsEngine = | ClearFluidParticlesMessage id -> Box2dNetPhysicsEngine.clearFluidParticlesMessage id physicsEngine | SetGravityMessage gravity -> Box2dNetPhysicsEngine.setGravityMessage gravity physicsEngine - static member makePhysicsContext gravity = + // NOTE: from Box2D documentation https://box2d.org/documentation/md_simulation.html + // update transforms - "Note that continuous collision does not generate events. Instead they are generated the next time step. However, continuous collision will issue a b2PreSolveFcn callback." + // we want penetration messages to be recorded on the same time step as penetration instead of the next, so we record it in the b2PreSolveFcn callback. + static let preSolveCallback = + b2PreSolveFcn (fun shapeIdA shapeIdB manifold context -> // can be called from multiple threads at once - write to concurrent collections only + let contactsTracker = context :?> Box2dNetPhysicsEngineContactsTracker + if not (contactsTracker.ExistingContacts.Contains (shapeIdA, shapeIdB)) then + let bodyShapeA = B2Shapes.b2Shape_GetUserData shapeIdA :?> BodyShapeIndex + let bodyShapeB = B2Shapes.b2Shape_GetUserData shapeIdB :?> BodyShapeIndex + let normal = Vector3 (manifold.normal.X, manifold.normal.Y, 0.0f) + contactsTracker.NewContacts.TryAdd + (struct (shapeIdA, shapeIdB), + { BodyShapeSource = bodyShapeA; BodyShapeTarget = bodyShapeB; Normal = normal }) + |> ignore + true) + + static member private makePhysicsContext gravity contactsTracker = let mutable worldDef = B2Types.b2DefaultWorldDef () worldDef.gravity <- gravity - B2Worlds.b2CreateWorld &worldDef + let world = B2Worlds.b2CreateWorld &worldDef + B2Worlds.b2World_SetPreSolveCallback (world, preSolveCallback, (contactsTracker : Box2dNetPhysicsEngineContactsTracker)) + world /// Make a physics engine. static member make gravity = - { PhysicsContextId = Box2dNetPhysicsEngine.makePhysicsContext (Box2dNetPhysicsEngine.toPhysicsV2 gravity) + let contactsTracker = + { NewContacts = ConcurrentDictionary () + ExistingContacts = HashSet () } + { PhysicsContextId = Box2dNetPhysicsEngine.makePhysicsContext (Box2dNetPhysicsEngine.toPhysicsV2 gravity) contactsTracker Bodies = Dictionary HashIdentity.Structural BodyGravityOverrides = Dictionary HashIdentity.Structural Joints = Dictionary HashIdentity.Structural BreakableJoints = Dictionary HashIdentity.Structural CreateBodyJointMessages = Dictionary HashIdentity.Structural FluidEmitters = Dictionary HashIdentity.Structural + ContactsTracker = contactsTracker IntegrationMessages = List () } :> PhysicsEngine interface PhysicsEngine with @@ -1570,7 +1609,7 @@ and [] Box2dNetPhysicsEngine = let body = physicsEngine.Bodies.[bodyId] if B2Bodies.b2Body_IsAwake body then let gravity = Box2dNetPhysicsEngine.toPhysicsV2 gravityOverride - B2Bodies.b2Body_SetLinearVelocity (body, B2Bodies.b2Body_GetLinearVelocity body + gravity * stepTime) + B2Bodies.b2Body_SetLinearVelocity (body, B2Bodies.b2Body_GetLinearVelocity body + gravity * stepTime) // NOTE: wakes body when awake not checked. // step the world B2Worlds.b2World_Step (physicsEngine.PhysicsContextId, stepTime, Constants.Physics.Collision2dSteps) @@ -1600,19 +1639,18 @@ and [] Box2dNetPhysicsEngine = LinearVelocity = Box2dNetPhysicsEngine.toPixelV3 (B2Bodies.b2Body_GetLinearVelocity transform.bodyId) AngularVelocity = v3 0.0f 0.0f (B2Bodies.b2Body_GetAngularVelocity transform.bodyId) }) - // collect penetrations for non-sensors - let contacts = B2Worlds.b2World_GetContactEvents physicsEngine.PhysicsContextId - for i in 0 .. dec contacts.beginCount do - let penetration = &contacts.beginEvents.[i] // NOTE: from Box2D documentation, rollingImpulse is always zero. - let bodyShapeA = B2Shapes.b2Shape_GetUserData penetration.shapeIdA :?> BodyShapeIndex - let bodyShapeB = B2Shapes.b2Shape_GetUserData penetration.shapeIdB :?> BodyShapeIndex - let normal = Vector3 (penetration.manifold.normal.X, penetration.manifold.normal.Y, 0.0f) - physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = bodyShapeA; BodyShapeTarget = bodyShapeB; Normal = normal }) - physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = bodyShapeB; BodyShapeTarget = bodyShapeA; Normal = -normal }) + // collect penetrations for non-sensors from preSolveCallback which exist on same time step as penetration unlike contact events which exist one step later. + for KeyValue (shapeIds, penetration) in physicsEngine.ContactsTracker.NewContacts do + physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeSource; BodyShapeTarget = penetration.BodyShapeTarget; Normal = penetration.Normal }) + physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeTarget; BodyShapeTarget = penetration.BodyShapeSource; Normal = -penetration.Normal }) + physicsEngine.ContactsTracker.ExistingContacts.Add shapeIds |> ignore + physicsEngine.ContactsTracker.NewContacts.Clear () // collect separations for non-sensors + let contacts = B2Worlds.b2World_GetContactEvents physicsEngine.PhysicsContextId for i in 0 .. dec contacts.endCount do let separation = &contacts.endEvents.[i] + physicsEngine.ContactsTracker.ExistingContacts.Remove (separation.shapeIdA, separation.shapeIdB) |> ignore let bodyShapeA = B2Shapes.b2Shape_GetUserData separation.shapeIdA :?> BodyShapeIndex let bodyShapeB = B2Shapes.b2Shape_GetUserData separation.shapeIdB :?> BodyShapeIndex physicsEngine.IntegrationMessages.Add (BodySeparationMessage { BodyShapeSource = bodyShapeA; BodyShapeTarget = bodyShapeB }) @@ -1749,7 +1787,7 @@ and [] Box2dNetPhysicsEngine = physicsEngine.BodyGravityOverrides.Clear () physicsEngine.CreateBodyJointMessages.Clear () let oldContext = physicsEngine.PhysicsContextId - physicsEngine.PhysicsContextId <- Box2dNetPhysicsEngine.makePhysicsContext (B2Worlds.b2World_GetGravity oldContext) + physicsEngine.PhysicsContextId <- Box2dNetPhysicsEngine.makePhysicsContext (B2Worlds.b2World_GetGravity oldContext) physicsEngine.ContactsTracker B2Worlds.b2DestroyWorld oldContext member physicsEngine.CleanUp () = diff --git a/Nu/Nu/Physics/PhysicsEngine.fs b/Nu/Nu/Physics/PhysicsEngine.fs index 3ecd735130..b80fcc693f 100644 --- a/Nu/Nu/Physics/PhysicsEngine.fs +++ b/Nu/Nu/Physics/PhysicsEngine.fs @@ -176,7 +176,7 @@ type FluidEmitterMessage = { FluidEmitterId : FluidEmitterId FluidParticles : FluidParticle SArray OutOfBoundsParticles : FluidParticle SArray - FluidCollisions : FluidCollision ConcurrentBag } + FluidCollisions : FluidCollision SArray } /// A message to the physics system to update the description of a fluid emitter. type UpdateFluidEmitterMessage = From b57e476bc5f3f6f4a018de0729a2f77f804feaf9 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 4 Nov 2025 11:08:02 -0500 Subject: [PATCH 02/52] *BREAKING:* Renamed Gravity.Gravity to Gravity.GravityOverride. Custom editing for Gravity property. Adjacent code clean-up. --- Nu/Nu/Physics/AetherPhysicsEngine.fs | 4 +- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 8 +-- Nu/Nu/Physics/JoltPhysicsEngine.fs | 6 +- Nu/Nu/Physics/PhysicsPrelude.fs | 14 ++--- Nu/Nu/World/WorldImGui.fs | 82 +++++++++++++++++--------- Projects/Sand Box 2d/ToyBox.fs | 14 ++--- 6 files changed, 78 insertions(+), 50 deletions(-) diff --git a/Nu/Nu/Physics/AetherPhysicsEngine.fs b/Nu/Nu/Physics/AetherPhysicsEngine.fs index b21e863554..5862edddb5 100644 --- a/Nu/Nu/Physics/AetherPhysicsEngine.fs +++ b/Nu/Nu/Physics/AetherPhysicsEngine.fs @@ -255,9 +255,9 @@ type private AetherFluidEmitter = // apply gravity to velocity match state.Gravity with | GravityWorld -> state.VelocityUnscaled <- state.VelocityUnscaled + gravityLocal - | GravityIgnore -> () + | GravityOverride gravity -> state.VelocityUnscaled <- state.VelocityUnscaled + gravity.V2 * clockDelta * descriptor.ParticleScale | GravityScale scale -> state.VelocityUnscaled <- state.VelocityUnscaled + gravityLocal * scale - | Gravity gravity -> state.VelocityUnscaled <- state.VelocityUnscaled + gravity.V2 * clockDelta * descriptor.ParticleScale) + | GravityIgnore -> ()) // assert loop completion assert loopResult.IsCompleted diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index 9395db064f..4e1a4cf6dc 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -248,9 +248,9 @@ type private Box2dNetFluidEmitter = // apply gravity to velocity match state.Gravity with | GravityWorld -> state.VelocityUnscaled <- state.VelocityUnscaled + gravityLocal - | GravityIgnore -> () + | GravityOverride gravity -> state.VelocityUnscaled <- state.VelocityUnscaled + gravity.V2 * clockDelta * descriptor.ParticleScale | GravityScale scale -> state.VelocityUnscaled <- state.VelocityUnscaled + gravityLocal * scale - | Gravity gravity -> state.VelocityUnscaled <- state.VelocityUnscaled + gravity.V2 * clockDelta * descriptor.ParticleScale) + | GravityIgnore -> ()) // assert loop completion assert loopResult.IsCompleted @@ -999,9 +999,9 @@ and [] Box2dNetPhysicsEngine = if bodyDef.``type`` = B2BodyType.b2_dynamicBody then match bodyProperties.Gravity with | GravityWorld -> bodyDef.gravityScale <- 1.0f; ValueNone - | GravityIgnore -> bodyDef.gravityScale <- 0.0f; ValueNone + | GravityOverride gravity -> bodyDef.gravityScale <- 0.0f; ValueSome gravity // NOTE: gravity overrides are handled by applying a manual force each step. | GravityScale scale -> bodyDef.gravityScale <- scale; ValueNone - | Gravity gravity -> bodyDef.gravityScale <- 0.0f; ValueSome gravity // NOTE: gravity overrides are handled by applying a manual force each step. + | GravityIgnore -> bodyDef.gravityScale <- 0.0f; ValueNone else ValueNone bodyDef.isBullet <- match bodyProperties.CollisionDetection with Continuous -> true | Discrete -> false bodyDef.isAwake <- bodyProperties.Awake diff --git a/Nu/Nu/Physics/JoltPhysicsEngine.fs b/Nu/Nu/Physics/JoltPhysicsEngine.fs index 6dcb3ffbee..48558b8699 100644 --- a/Nu/Nu/Physics/JoltPhysicsEngine.fs +++ b/Nu/Nu/Physics/JoltPhysicsEngine.fs @@ -595,12 +595,12 @@ and [] JoltPhysicsEngine = bodyCreationSettings.GravityFactor <- match bodyProperties.Gravity with | GravityWorld -> 1.0f - | GravityIgnore -> 0.0f - | GravityScale scale -> scale - | Gravity gravity -> + | GravityOverride gravity -> // NOTE: this needs manual bookkeeping like for characters. Log.warnOnce "Individual gravity configuration is unsupported for non-characters in JoltPhysicsEngine; interpreting as a scale by magnitude instead." gravity.Magnitude + | GravityScale scale -> scale + | GravityIgnore -> 0.0f bodyCreationSettings.MotionQuality <- match bodyProperties.CollisionDetection with | Discrete -> MotionQuality.Discrete diff --git a/Nu/Nu/Physics/PhysicsPrelude.fs b/Nu/Nu/Physics/PhysicsPrelude.fs index 960061f814..dad7139835 100644 --- a/Nu/Nu/Physics/PhysicsPrelude.fs +++ b/Nu/Nu/Physics/PhysicsPrelude.fs @@ -412,21 +412,21 @@ type VehicleProperties = | VehiclePropertiesAbsent | VehiclePropertiesBox2D | VehiclePropertiesJolt of JoltPhysicsSharp.VehicleConstraintSettings - -/// Describes whether a body should follow a scale of world gravity (Default = 1, None = 0) or use an override. + +/// Describes the gravitational property of a body. type Gravity = | GravityWorld - | GravityIgnore + | GravityOverride of Vector3 | GravityScale of single - | Gravity of Vector3 - + | GravityIgnore + /// Compute local gravity based on the given world gravity. static member localize gravityWorld gravity = match gravity with | GravityWorld -> gravityWorld - | GravityIgnore -> v3Zero + | GravityOverride gravity -> gravity | GravityScale scale -> gravityWorld * scale - | Gravity gravity -> gravity + | GravityIgnore -> v3Zero /// The properties needed to describe the physical part of a body. type BodyProperties = diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index 37bdc9cc98..3452b2dca1 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -721,13 +721,13 @@ module WorldImGui = if ImGui.IsItemFocused () then context.FocusProperty () let mutable index = match substance with Mass _ -> 0 | Density _ -> 1 ImGui.SameLine () - let result = + let (edited, substance) = if ImGui.Combo (name, &index, [|nameof Mass; nameof Density|], 2) || edited then let substance = match index with 0 -> Mass scalar | 1 -> Density scalar | _ -> failwithumf () - (false, true, substance :> obj) - else (false, false, substance :> obj) + (true, substance :> obj) + else (false, substance :> obj) if ImGui.IsItemFocused () then context.FocusProperty () - result + (false, edited, substance) | :? Animation as animation -> let tryReplaceAnimationName (fieldInfo : PropertyInfo) (field : obj) = match (fieldInfo.Name, context.SelectedEntityOpt) with @@ -760,7 +760,7 @@ module WorldImGui = | :? Material as material -> World.imGuiEditPropertyRecord false name (typeof) material context world | :? Justification as justification -> - let (promoted, caseNameEdited, caseName) = World.imGuiSelectCase name ty justification context + let (_, caseNameEdited, caseName) = World.imGuiSelectCase name ty justification context let justification = if caseNameEdited then match caseName with @@ -768,21 +768,21 @@ module WorldImGui = | nameof Unjustified -> Unjustified true | _ -> failwithumf () else justification - match justification with - | Justified (h, v) -> - ImGui.Indent () - let (promoted2, edited, h) = World.imGuiEditProperty "JustificationH" (getType h) h context world - let (promoted3, edited2, v) = World.imGuiEditProperty "JustificationV" (getType v) v context world - ImGui.Text "(wrapping unavailable when justified)" - ImGui.Unindent () - (promoted || promoted2 || promoted3, caseNameEdited || edited || edited2, Justified (h :?> JustificationH, v :?> JustificationV)) - | Unjustified wrapped -> - ImGui.Indent () - let (promoted2, edited, wrapped) = World.imGuiEditProperty "Wrapped" (getType wrapped) wrapped context world - ImGui.Unindent () - (promoted || promoted2, caseNameEdited || edited, Unjustified (wrapped :?> bool)) + ImGui.Indent () + let (edited, justification) = + match justification with + | Justified (h, v) -> + let (_, edited, h) = World.imGuiEditProperty "JustificationH" (getType h) h context world + let (_, edited2, v) = World.imGuiEditProperty "JustificationV" (getType v) v context world + ImGui.Text "(wrapping unavailable when justified)" + (caseNameEdited || edited || edited2, Justified (h :?> JustificationH, v :?> JustificationV)) + | Unjustified wrapped -> + let (_, edited, wrapped) = World.imGuiEditProperty "Wrapped" (getType wrapped) wrapped context world + (caseNameEdited || edited, Unjustified (wrapped :?> bool)) + ImGui.Unindent () + (false, edited, justification) | :? FlowLimit as limit -> - let (promoted, caseNameEdited, caseName) = World.imGuiSelectCase name ty limit context + let (_, caseNameEdited, caseName) = World.imGuiSelectCase name ty limit context let limit = if caseNameEdited then match caseName with @@ -791,14 +791,42 @@ module WorldImGui = | nameof FlowTo -> FlowTo 32.0f | _ -> failwithumf () else limit - match limit with - | FlowParent -> (promoted, caseNameEdited, limit) - | FlowUnlimited -> (promoted, caseNameEdited, limit) - | FlowTo limit -> - let (promoted2, edited, limit) = World.imGuiEditProperty "Limit" (getType limit) limit context world - (promoted || promoted2, caseNameEdited || edited, FlowTo (limit :?> single)) + ImGui.Indent () + let (edited, value) = + match limit with + | FlowParent -> (caseNameEdited, limit) + | FlowUnlimited -> (caseNameEdited, limit) + | FlowTo limit -> + let (_, edited, limit) = World.imGuiEditProperty "Limit" (getType limit) limit context world + (caseNameEdited || edited, FlowTo (limit :?> single)) + ImGui.Unindent () + (false, edited, value) + | :? Gravity as gravity -> + let (_, caseNameEdited, caseName) = World.imGuiSelectCase name ty gravity context + let gravity = + if caseNameEdited then + match caseName with + | nameof GravityWorld -> GravityWorld + | nameof GravityOverride -> GravityOverride Constants.Physics.GravityDefault + | nameof GravityScale -> GravityScale 1.0f + | nameof GravityIgnore -> GravityIgnore + | _ -> failwithumf () + else gravity + ImGui.Indent () + let (edited, gravity) = + match gravity with + | GravityWorld -> (caseNameEdited, gravity) + | GravityOverride gravity -> + let (_, edited, gravity) = World.imGuiEditProperty "Gravity" (getType gravity) gravity context world + (caseNameEdited || edited, GravityOverride (gravity :?> Vector3)) + | GravityScale scale -> + let (_, edited, scale) = World.imGuiEditProperty "Scale" (getType scale) scale context world + (caseNameEdited || edited, GravityScale (scale :?> single)) + | GravityIgnore -> (caseNameEdited, gravity) + ImGui.Unindent () + (false, edited, gravity) | :? Layout as layout -> - let (promoted, caseNameEdited, caseName) = World.imGuiSelectCase name ty layout context + let (_, caseNameEdited, caseName) = World.imGuiSelectCase name ty layout context let layout = if caseNameEdited then match caseName with @@ -830,7 +858,7 @@ module WorldImGui = (caseNameEdited || edited || edited2 || edited3, Grid (dims :?> Vector2i, flowDirectionOpt :?> FlowDirection option, resizeChildren :?> bool)) | Manual -> (caseNameEdited, layout) ImGui.Unindent () - (promoted, edited, layout) + (false, edited, layout) | :? Lighting3dConfig as lighting3dConfig -> let mutable lighting3dEdited = false let mutable lightCutoffMargin = lighting3dConfig.LightCutoffMargin diff --git a/Projects/Sand Box 2d/ToyBox.fs b/Projects/Sand Box 2d/ToyBox.fs index e60fcb09eb..6287bcaa68 100644 --- a/Projects/Sand Box 2d/ToyBox.fs +++ b/Projects/Sand Box 2d/ToyBox.fs @@ -755,17 +755,17 @@ type ToyBoxDispatcher () = ("^", defaultGravity.Transform (Quaternion.CreateFromAngle2d MathF.PI)) ("<", defaultGravity.Transform (Quaternion.CreateFromAngle2d -MathF.PI_OVER_2))] |> List.randomShuffle - |> List.cons ("v", defaultGravity) // Always start with the default down gravity + |> List.cons ("v", defaultGravity) // always start with the default down gravity static let generateAvatarGravities (world : World) = let defaultGravity = World.getGravityDefault2d world - [(">", Gravity (defaultGravity.Transform (Quaternion.CreateFromAngle2d MathF.PI_OVER_2))) - ("0", Gravity v3Zero) - ("^", Gravity (defaultGravity.Transform (Quaternion.CreateFromAngle2d MathF.PI))) - ("<", Gravity (defaultGravity.Transform (Quaternion.CreateFromAngle2d -MathF.PI_OVER_2))) - ("v", Gravity defaultGravity)] + [(">", GravityOverride (defaultGravity.Transform (Quaternion.CreateFromAngle2d MathF.PI_OVER_2))) + ("0", GravityOverride v3Zero) + ("^", GravityOverride (defaultGravity.Transform (Quaternion.CreateFromAngle2d MathF.PI))) + ("<", GravityOverride (defaultGravity.Transform (Quaternion.CreateFromAngle2d -MathF.PI_OVER_2))) + ("v", GravityOverride defaultGravity)] |> List.randomShuffle - |> List.cons ("World", GravityWorld) // Always start with GravityDefault + |> List.cons ("World", GravityWorld) // always start with GravityDefault // here we define default property values static member Properties = From 836d2d402022c1f3a0188a39bf61c4e72e292cb6 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 4 Nov 2025 11:11:00 -0500 Subject: [PATCH 03/52] Comment fix. --- Projects/Sand Box 2d/ToyBox.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Sand Box 2d/ToyBox.fs b/Projects/Sand Box 2d/ToyBox.fs index 6287bcaa68..0926169b0b 100644 --- a/Projects/Sand Box 2d/ToyBox.fs +++ b/Projects/Sand Box 2d/ToyBox.fs @@ -765,7 +765,7 @@ type ToyBoxDispatcher () = ("<", GravityOverride (defaultGravity.Transform (Quaternion.CreateFromAngle2d -MathF.PI_OVER_2))) ("v", GravityOverride defaultGravity)] |> List.randomShuffle - |> List.cons ("World", GravityWorld) // always start with GravityDefault + |> List.cons ("World", GravityWorld) // always start with GravityWorld // here we define default property values static member Properties = From e27cd0daea8f65f6324793dc3f6acbe41fd983c6 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 4 Nov 2025 11:18:32 -0500 Subject: [PATCH 04/52] Renamed 'global fog' concept to 'distance fog'. --- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- Nu/Nu/World/WorldImGui.fs | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- .../Assets/Default/PhysicallyBasedDeferredComposition.glsl | 2 +- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 2 +- .../Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl | 2 +- 52 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index 3452b2dca1..3030ac7b11 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -943,7 +943,7 @@ module WorldImGui = lighting3dEdited <- ImGui.SliderFloat ("Tone Map Saturation", &toneMapSaturation, 0.0f, 2.0f) || lighting3dEdited; if ImGui.IsItemFocused () then context.FocusProperty () if toneMapType = ReinhardExtendedToneMap.Enumerate then lighting3dEdited <- ImGui.SliderFloat ("Tone Map White Point", &toneMapWhitePoint, 0.0f, 20.0f) || lighting3dEdited; if ImGui.IsItemFocused () then context.FocusProperty () - ImGui.Text "Global Fog" + ImGui.Text "Distance Fog" lighting3dEdited <- ImGui.Checkbox ("Fog Enabled", &fogEnabled) || lighting3dEdited; if ImGui.IsItemFocused () then context.FocusProperty () lighting3dEdited <- ImGui.Combo ("Fog Type", &fogType, FogType.Names, FogType.Names.Length) || lighting3dEdited; if ImGui.IsItemFocused () then context.FocusProperty () if fogType = LinearFog.Enumerate then diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredComposition.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredComposition.glsl index 955fa8d2aa..fde0945d6b 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredComposition.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredComposition.glsl @@ -51,7 +51,7 @@ void main() vec3 fogAccum = texture(fogAccumTexture, texCoordsOut, 0).xyz; vec3 color = texture(colorTexture, texCoordsOut, 0).xyz + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled vec4 position = depthToPosition(depth, texCoordsOut); float distance = length(position.xyz - eyeCenter); if (fogEnabled == 1) diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b747954d08..b534beef6f 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -1049,7 +1049,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl index c326e319c4..b235d2acbd 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -1030,7 +1030,7 @@ void main() // compute color composition vec3 color = lightAccumDiffuse + diffuse + emission * albedo.rgb + lightAccumSpecular + specular + fogAccum; - // compute and apply global fog when enabled + // compute and apply distance fog when enabled if (fogEnabled == 1) { switch (fogType) From cd7d7f4e703caeccadcffeed3b6a7c1b58248e57 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 4 Nov 2025 11:37:57 -0500 Subject: [PATCH 05/52] Fixed custom gravity accumulating velocity when grounded in JoltPhysicsEngine. Removed support for overridden gravity for Jolt characters as well. --- Nu/Nu/Physics/JoltPhysicsEngine.fs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Nu/Nu/Physics/JoltPhysicsEngine.fs b/Nu/Nu/Physics/JoltPhysicsEngine.fs index 48558b8699..25907df609 100644 --- a/Nu/Nu/Physics/JoltPhysicsEngine.fs +++ b/Nu/Nu/Physics/JoltPhysicsEngine.fs @@ -596,8 +596,7 @@ and [] JoltPhysicsEngine = match bodyProperties.Gravity with | GravityWorld -> 1.0f | GravityOverride gravity -> - // NOTE: this needs manual bookkeeping like for characters. - Log.warnOnce "Individual gravity configuration is unsupported for non-characters in JoltPhysicsEngine; interpreting as a scale by magnitude instead." + Log.warnOnce "Gravity override is unsupported in JoltPhysicsEngine; interpreting as a scale by magnitude instead." gravity.Magnitude | GravityScale scale -> scale | GravityIgnore -> 0.0f @@ -1506,7 +1505,14 @@ and [] JoltPhysicsEngine = let characterLayer = Constants.Physics.ObjectLayerMoving for character in physicsEngine.Characters.Values do let characterUserData = physicsEngine.CharacterUserData.[character.ID] - let characterGravity = Gravity.localize physicsEngine.PhysicsContext.Gravity characterUserData.CharacterGravity + let characterGravityFactor = + match characterUserData.CharacterGravity with + | GravityWorld -> 1.0f + | GravityOverride gravity -> + Log.warnOnce "Gravity override is unsupported in JoltPhysicsEngine; interpreting as a scale by magnitude instead." + gravity.Magnitude + | GravityScale scale -> scale + | GravityIgnore -> 0.0f let characterProperties = characterUserData.CharacterProperties let mutable characterUpdateSettings = ExtendedUpdateSettings @@ -1517,9 +1523,13 @@ and [] JoltPhysicsEngine = WalkStairsMinStepForward = characterProperties.StairStepForwardMin, WalkStairsCosAngleForwardContact = characterProperties.StairCosAngleForwardContact) character.LinearVelocity <- - if character.GroundState = GroundState.OnGround - then character.LinearVelocity.MapY (max 0.0f) - else character.LinearVelocity + characterGravity * stepTime.Seconds + if character.GroundState = GroundState.OnGround then + if characterGravityFactor < 0.0f + then character.LinearVelocity.MapY (min 0.0f) + else character.LinearVelocity.MapY (max 0.0f) + else + let characterGravity = physicsEngine.PhysicsContext.Gravity * characterGravityFactor + character.LinearVelocity + characterGravity * stepTime.Seconds character.ExtendedUpdate (stepTime.Seconds, characterUpdateSettings, &characterLayer, physicsEngine.PhysicsContext) // update constraints From 6a4cc80b6f6fc606196b176906d7172882d8729e Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 4 Nov 2025 14:39:44 -0500 Subject: [PATCH 06/52] Using |= as it more generally appropriate for initial box position. --- Projects/Jump Box/JumpBox.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Jump Box/JumpBox.fs b/Projects/Jump Box/JumpBox.fs index 0ae21a919d..59dbf2d4aa 100644 --- a/Projects/Jump Box/JumpBox.fs +++ b/Projects/Jump Box/JumpBox.fs @@ -31,7 +31,7 @@ type JumpBoxDispatcher () = World.doBlock2d "Block" [Entity.Position .= v3 128.0f -64.0f 0.0f] world |> ignore // declare a box and then handle its body interactions for the frame - let (boxBodyId, results) = World.doBox2d "Box" [Entity.Position .= v3 128.0f 64.0f 0.0f] world + let (boxBodyId, results) = World.doBox2d "Box" [Entity.Position |= v3 128.0f 64.0f 0.0f] world for result in results do match result with | BodyPenetrationData _ -> game.Collisions.Map inc world From 419002ce9fbb688c214948c5916fd087d617c946 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 4 Nov 2025 18:15:18 -0500 Subject: [PATCH 07/52] Fixed tab styling while perusing through shader code. --- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- .../Default/PhysicallyBasedDeferredLighting.glsl | 10 +++++----- 17 files changed, 85 insertions(+), 85 deletions(-) diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 77e145e50d..90463f4df8 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -29,11 +29,11 @@ const float SHADOW_CASCADE_DENSITY_BONUS = 0.5; const float SHADOW_FOV_MAX = 2.1; const vec4 SSVF_DITHERING[4] = -vec4[]( - vec4(0.0, 0.5, 0.125, 0.625), - vec4(0.75, 0.22, 0.875, 0.375), - vec4(0.1875, 0.6875, 0.0625, 0.5625), - vec4(0.9375, 0.4375, 0.8125, 0.3125)); + vec4[]( + vec4(0.0, 0.5, 0.125, 0.625), + vec4(0.75, 0.22, 0.875, 0.375), + vec4(0.1875, 0.6875, 0.0625, 0.5625), + vec4(0.9375, 0.4375, 0.8125, 0.3125)); uniform vec3 eyeCenter; uniform mat4 view; From 4b9381ef16d9f5951075b20e08ce205c6920d949 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 5 Nov 2025 15:51:49 -0500 Subject: [PATCH 08/52] Fixed #1209. --- Nu/Nu.Gaia/Gaia.fs | 67 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 3777f51bb2..332dd86405 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1857,39 +1857,40 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.Text (entity.Name + if ImGui.IsCtrlDown () then " (Copy)" else "") ImGui.SetDragDropPayload ("Entity", IntPtr.Zero, 0u) |> ignore ImGui.EndDragDropSource () - if entity.GetExists world && entity.Has world then // check for existence since entity may have been deleted just above - let frozen = entity.GetFrozen world - let (text, color) = if frozen then ("Thaw", Color.CornflowerBlue) else ("Freeze", Color.DarkRed) - ImGui.SameLine () - ImGui.PushStyleColor (ImGuiCol.Button, color.Abgr) - ImGui.PushID ("##frozen" + scstringMemo entity) - if ImGui.SmallButton text then - let frozen = not frozen - snapshot (SetEntityFrozen frozen) world - entity.SetFrozen frozen world - ImGui.PopID () - ImGui.PopStyleColor () - let hasPropagationTargets = entity.HasPropagationTargets world - let hasPropagationDescriptorOpt = Option.isSome (entity.GetPropagatedDescriptorOpt world) - if hasPropagationTargets || hasPropagationDescriptorOpt then - ImGui.SameLine () - ImGui.PushID ("##push" + scstringMemo entity) - if ImGui.SmallButton "Push" then - propagateEntityStructure entity world - ImGui.PopID () - if ImGui.IsItemHovered ImGuiHoveredFlags.DelayNormal && ImGui.BeginTooltip () then - ImGui.Text "Propagate entity structure to all targets, preserving propagation data." - ImGui.EndTooltip () - ImGui.SameLine () - ImGui.PushID ("##wipe" + scstringMemo entity) - if ImGui.SmallButton "Wipe" then - snapshot WipePropagationTargets world - World.clearPropagationTargets entity world - entity.SetPropagatedDescriptorOpt None world - ImGui.PopID () - if ImGui.IsItemHovered ImGuiHoveredFlags.DelayNormal && ImGui.BeginTooltip () then - ImGui.Text "Clear entity structure propagation targets, wiping any propagated descriptor data." - ImGui.EndTooltip () + if entity.GetExists world then // check for existence since entity may have been deleted just above + if entity.Has world then + let frozen = entity.GetFrozen world + let (text, color) = if frozen then ("Thaw", Color.CornflowerBlue) else ("Freeze", Color.DarkRed) + ImGui.SameLine () + ImGui.PushStyleColor (ImGuiCol.Button, color.Abgr) + ImGui.PushID ("##frozen" + scstringMemo entity) + if ImGui.SmallButton text then + let frozen = not frozen + snapshot (SetEntityFrozen frozen) world + entity.SetFrozen frozen world + ImGui.PopID () + ImGui.PopStyleColor () + let hasPropagationTargets = entity.HasPropagationTargets world + let hasPropagationDescriptorOpt = Option.isSome (entity.GetPropagatedDescriptorOpt world) + if hasPropagationTargets || hasPropagationDescriptorOpt then + ImGui.SameLine () + ImGui.PushID ("##push" + scstringMemo entity) + if ImGui.SmallButton "Push" then + propagateEntityStructure entity world + ImGui.PopID () + if ImGui.IsItemHovered ImGuiHoveredFlags.DelayNormal && ImGui.BeginTooltip () then + ImGui.Text "Propagate entity structure to all targets, preserving propagation data." + ImGui.EndTooltip () + ImGui.SameLine () + ImGui.PushID ("##wipe" + scstringMemo entity) + if ImGui.SmallButton "Wipe" then + snapshot WipePropagationTargets world + World.clearPropagationTargets entity world + entity.SetPropagatedDescriptorOpt None world + ImGui.PopID () + if ImGui.IsItemHovered ImGuiHoveredFlags.DelayNormal && ImGui.BeginTooltip () then + ImGui.Text "Clear entity structure propagation targets, wiping any propagated descriptor data." + ImGui.EndTooltip () expanded let rec private imGuiEntityHierarchy (entity : Entity) world = From 0674eef1d321325ca86d975210ea58b28dee2a0e Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 5 Nov 2025 22:38:39 -0500 Subject: [PATCH 09/52] Waking joint bodies on joint commands. --- Nu/Nu/Physics/AetherPhysicsEngine.fs | 7 ++----- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Nu/Nu/Physics/AetherPhysicsEngine.fs b/Nu/Nu/Physics/AetherPhysicsEngine.fs index 5862edddb5..900fbc6472 100644 --- a/Nu/Nu/Physics/AetherPhysicsEngine.fs +++ b/Nu/Nu/Physics/AetherPhysicsEngine.fs @@ -920,8 +920,7 @@ and [] AetherPhysicsEngine = static member private createBodyJointInternal bodyJointProperties bodyJointId physicsEngine = let resultOpt = match bodyJointProperties.BodyJoint with - | EmptyJoint -> - None + | EmptyJoint -> None | AetherBodyJoint bodyJoint -> let bodyId = bodyJointProperties.BodyJointTarget let body2Id = bodyJointProperties.BodyJointTarget2 @@ -930,9 +929,7 @@ and [] AetherPhysicsEngine = let joint = bodyJoint.CreateBodyJoint AetherPhysicsEngine.toPhysics AetherPhysicsEngine.toPhysicsV2 body body2 Some (joint, body, Some body2) | _ -> None - | _ -> - Log.warn ("Joint type '" + getCaseName bodyJointProperties.BodyJoint + "' not implemented for AetherPhysicsEngine.") - None + | _ -> Log.warn ("Joint type '" + getCaseName bodyJointProperties.BodyJoint + "' not implemented for AetherPhysicsEngine."); None match resultOpt with | Some (joint, body, body2Opt) -> joint.Tag <- bodyJointId diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index 4e1a4cf6dc..e415f2b76f 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -1069,8 +1069,7 @@ and [] Box2dNetPhysicsEngine = static member private createBodyJointInternal bodyJointProperties bodyJointId physicsEngine = if bodyJointProperties.BodyJointEnabled && not bodyJointProperties.Broken then match bodyJointProperties.BodyJoint with - | EmptyJoint -> - () + | EmptyJoint -> () | Box2dNetBodyJoint bodyJoint -> let bodyId = bodyJointProperties.BodyJointTarget let body2Id = bodyJointProperties.BodyJointTarget2 @@ -1088,8 +1087,7 @@ and [] Box2dNetPhysicsEngine = | None -> () else Log.warn ("Could not add body joint for '" + scstring bodyJointId + "' as it already exists.") | _ -> () - | _ -> - Log.warn ("Joint type '" + getCaseName bodyJointProperties.BodyJoint + "' not implemented for Box2dNetPhysicsEngine.") + | _ -> Log.warn ("Joint type '" + getCaseName bodyJointProperties.BodyJoint + "' not implemented for Box2dNetPhysicsEngine.") static member private createBodyJoint (createBodyJointMessage : CreateBodyJointMessage) physicsEngine = @@ -1188,6 +1186,9 @@ and [] Box2dNetPhysicsEngine = | B2JointType.b2_revoluteJoint -> B2RevoluteJoints.b2RevoluteJoint_EnableMotor (joint, setBodyJointMotorEnabledMessage.MotorEnabled) | B2JointType.b2_wheelJoint -> B2WheelJoints.b2WheelJoint_EnableMotor (joint, setBodyJointMotorEnabledMessage.MotorEnabled) | _ -> () + if setBodyJointMotorEnabledMessage.MotorEnabled then + B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyA joint, true) // force bodies awake + B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyB joint, true) | (false, _) -> () static member private setBodyJointMotorSpeed (setBodyJointMotorSpeedMessage : SetBodyJointMotorSpeedMessage) physicsEngine = @@ -1199,13 +1200,19 @@ and [] Box2dNetPhysicsEngine = | B2JointType.b2_revoluteJoint -> B2RevoluteJoints.b2RevoluteJoint_SetMotorSpeed (joint, setBodyJointMotorSpeedMessage.MotorSpeed) | B2JointType.b2_wheelJoint -> B2WheelJoints.b2WheelJoint_SetMotorSpeed (joint, setBodyJointMotorSpeedMessage.MotorSpeed) | _ -> () + if setBodyJointMotorSpeedMessage.MotorSpeed <> 0.0f then + B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyA joint, true) // force bodies awake + B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyB joint, true) | (false, _) -> () static member private setBodyJointTargetAngle (setBodyJointTargetAngleMessage : SetBodyJointTargetAngleMessage) physicsEngine = match physicsEngine.Joints.TryGetValue setBodyJointTargetAngleMessage.BodyJointId with | (true, joint) -> match B2Joints.b2Joint_GetType joint with - | B2JointType.b2_revoluteJoint -> B2RevoluteJoints.b2RevoluteJoint_SetTargetAngle (joint, setBodyJointTargetAngleMessage.TargetAngle) + | B2JointType.b2_revoluteJoint -> + B2RevoluteJoints.b2RevoluteJoint_SetTargetAngle (joint, setBodyJointTargetAngleMessage.TargetAngle) + B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyA joint, true) // force bodies awake + B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyB joint, true) | _ -> () | (false, _) -> () From 7979c26e2a8a7d9d0321918e029266a01d79121f Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 5 Nov 2025 23:17:38 -0500 Subject: [PATCH 10/52] Now creating 2D physics engine dependencies via NuPlugin factory methods. --- Nu/Nu/Core/Constants.fs | 1 - Nu/Nu/Core/Math.fs | 5 ----- Nu/Nu/World/World.fs | 5 +---- Nu/Nu/World/WorldConfigure.fs | 1 - Nu/Nu/World/WorldImGui.fs | 25 +------------------------ Nu/Nu/World/WorldPhysics.fs | 3 +++ Nu/Nu/World/WorldTypes.fs | 23 +++++++++++++++++++++++ 7 files changed, 28 insertions(+), 35 deletions(-) diff --git a/Nu/Nu/Core/Constants.fs b/Nu/Nu/Core/Constants.fs index 2bce4c474e..7b023daefd 100644 --- a/Nu/Nu/Core/Constants.fs +++ b/Nu/Nu/Core/Constants.fs @@ -343,7 +343,6 @@ module Physics = let [] FrictionDefault = 0.5f let [] AngularDampingDefault = 0.2f let [] CollisionWildcard = "*" - let [] mutable PhysicsEngine2d = match ConfigurationManager.AppSettings.["PhysicsEngine2d"] with null -> Aether | value -> scvalue value let [] mutable Collision2dSteps = match ConfigurationManager.AppSettings.["Collision2dSteps"] with null -> 4 | value -> scvalue value let [] mutable Collision3dBodiesMax = match ConfigurationManager.AppSettings.["Collision3dBodiesMax"] with null -> 65536 | value -> scvalue value let [] mutable Collision3dBodyPairsMax = match ConfigurationManager.AppSettings.["Collision3dBodyPairsMax"] with null -> 32768 | value -> scvalue value diff --git a/Nu/Nu/Core/Math.fs b/Nu/Nu/Core/Math.fs index 1052286be9..b885e9ed95 100644 --- a/Nu/Nu/Core/Math.fs +++ b/Nu/Nu/Core/Math.fs @@ -1724,11 +1724,6 @@ type [] ScatterType = | FoliageScatter -> 0.2f | WaxScatter -> 0.3f -/// The type of 2D physics engine to use. -type [] PhysicsEngine2dType = - | Aether - | Box2dNet - [] module Math = diff --git a/Nu/Nu/World/World.fs b/Nu/Nu/World/World.fs index 267c145125..0a64b82fc0 100644 --- a/Nu/Nu/World/World.fs +++ b/Nu/Nu/World/World.fs @@ -507,10 +507,7 @@ module WorldModule4 = // make the world's subsystems, loading initial packages where applicable let imGui = ImGui (false, windowViewport.Bounds.Size) - let physicsEngine2d = - match Constants.Physics.PhysicsEngine2d with - | Aether -> AetherPhysicsEngine.make (Constants.Physics.GravityDefault * Constants.Engine.Meter2d) - | Box2dNet -> Box2dNetPhysicsEngine.make (Constants.Physics.GravityDefault * Constants.Engine.Meter2d) + let physicsEngine2d = plugin.CreatePhysicsEngine2d () let physicsEngine3d = JoltPhysicsEngine.make Constants.Physics.GravityDefault let joltDebugRendererImGuiOpt = new JoltDebugRendererImGui () let rendererProcess = diff --git a/Nu/Nu/World/WorldConfigure.fs b/Nu/Nu/World/WorldConfigure.fs index d2aa33645f..e8109f2916 100644 --- a/Nu/Nu/World/WorldConfigure.fs +++ b/Nu/Nu/World/WorldConfigure.fs @@ -50,7 +50,6 @@ module Configure = | nameof Constants.Render.ShadowCascadeLimits -> Constants.Render.ShadowCascadeLimits <- scvalue value | nameof Constants.Render.ShadowCascadeMarginRatio -> Constants.Render.ShadowCascadeMarginRatio <- scvalue value | nameof Constants.Render.ShadowCascadeMarginRatioCull -> Constants.Render.ShadowCascadeMarginRatioCull <- scvalue value - | nameof Constants.Physics.PhysicsEngine2d -> Constants.Physics.PhysicsEngine2d <- scvalue value | nameof Constants.Physics.Collision2dSteps -> Constants.Physics.Collision2dSteps <- scvalue value | nameof Constants.Physics.Collision3dBodiesMax -> Constants.Physics.Collision3dBodiesMax <- scvalue value | nameof Constants.Physics.Collision3dBodyPairsMax -> Constants.Physics.Collision3dBodyPairsMax <- scvalue value diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index 3030ac7b11..d975ffd9f0 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -1232,30 +1232,7 @@ module WorldImGui2 = let segments = Dictionary () let circles = Dictionary () let physicsEngine2d = World.getPhysicsEngine2d world - let renderContext : PhysicsEngineRenderContext = - match Constants.Physics.PhysicsEngine2d with - | Aether -> - { new AetherPhysicsEngineRenderContext with - override this.DrawLine (start : Vector2, stop : Vector2, color) = - match segments.TryGetValue color with - | (true, segmentList) -> segmentList.Add (start, stop) - | (false, _) -> segments.Add (color, List [struct (start, stop)]) - override this.DrawCircle (center : Vector2, radius, color) = - match circles.TryGetValue struct (color, radius) with - | (true, circleList) -> circleList.Add center - | (false, _) -> circles.Add (struct (color, radius), List [center]) - override _.EyeBounds = world.Eye2dBounds } - | Box2dNet -> - { new Box2dNetPhysicsEngineRenderContext with - override this.DrawLine (start : Vector2, stop : Vector2, color) = - match segments.TryGetValue color with - | (true, segmentList) -> segmentList.Add (start, stop) - | (false, _) -> segments.Add (color, List [struct (start, stop)]) - override this.DrawCircle (center : Vector2, radius, color) = - match circles.TryGetValue struct (color, radius) with - | (true, circleList) -> circleList.Add center - | (false, _) -> circles.Add (struct (color, radius), List [center]) - override _.EyeBounds = world.Eye2dBounds } + let renderContext = World.createPhysicsEngine2dRenderContext segments circles world physicsEngine2d.TryRender renderContext for struct (color, segmentList) in segments.Pairs' do World.imGuiSegments2d false segmentList 1.0f color world diff --git a/Nu/Nu/World/WorldPhysics.fs b/Nu/Nu/World/WorldPhysics.fs index 77f212aa5b..00d8b26da1 100644 --- a/Nu/Nu/World/WorldPhysics.fs +++ b/Nu/Nu/World/WorldPhysics.fs @@ -21,6 +21,9 @@ module WorldPhysics = static member internal getRendererPhysics3dOpt (world : World) = world.Subsystems.RendererPhysics3dOpt + static member internal createPhysicsEngine2dRenderContext segments circles (world : World) = + world.WorldExtension.Plugin.CreatePhysicsEngine2dRenderContext segments circles world.Eye2dBounds + /// Localize a primitive body shape to a specific size; non-primitive body shapes are unaffected. static member localizePrimitiveBodyShape (size : Vector3) (bodyShape : BodyShape) = Physics.localizePrimitiveBodyShape size bodyShape diff --git a/Nu/Nu/World/WorldTypes.fs b/Nu/Nu/World/WorldTypes.fs index b29c2cb0d2..52fbf1f3c5 100644 --- a/Nu/Nu/World/WorldTypes.fs +++ b/Nu/Nu/World/WorldTypes.fs @@ -2227,6 +2227,29 @@ and [] NuPlugin () = abstract InitialPackages : string list default this.InitialPackages = [] + /// Create the 2D physics engine for the engine to use. + abstract CreatePhysicsEngine2d : unit -> PhysicsEngine + default this.CreatePhysicsEngine2d () = + AetherPhysicsEngine.make (Constants.Physics.GravityDefault * Constants.Engine.Meter2d) + + /// Create the 2D physics engine renderer for the engine to use for the current frame. + abstract CreatePhysicsEngine2dRenderContext : + segments : Dictionary -> + circles : Dictionary -> + eyeBounds : Box2 -> + PhysicsEngineRenderContext + default this.CreatePhysicsEngine2dRenderContext segments circles eyeBounds = + { new AetherPhysicsEngineRenderContext with + override this.DrawLine (start : Vector2, stop : Vector2, color) = + match segments.TryGetValue color with + | (true, segmentList) -> segmentList.Add (start, stop) + | (false, _) -> segments.Add (color, List [struct (start, stop)]) + override this.DrawCircle (center : Vector2, radius, color) = + match circles.TryGetValue struct (color, radius) with + | (true, circleList) -> circleList.Add center + | (false, _) -> circles.Add (struct (color, radius), List [center]) + override _.EyeBounds = eyeBounds } + /// Clean-up any user-defined resources of the plugin, such with shutting down a Steamworks API. abstract CleanUp : unit -> unit default this.CleanUp () = () From 4c948eab0651a8f9f1c8cbcc22df26a550f45464 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 6 Nov 2025 00:49:46 -0500 Subject: [PATCH 11/52] Renamed NuPlugin Create methods to Make for consistency. --- Nu/Nu/World/World.fs | 2 +- Nu/Nu/World/WorldImGui.fs | 2 +- Nu/Nu/World/WorldPhysics.fs | 4 ++-- Nu/Nu/World/WorldTypes.fs | 46 ++++++++++++++++++------------------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Nu/Nu/World/World.fs b/Nu/Nu/World/World.fs index 0a64b82fc0..1c78528493 100644 --- a/Nu/Nu/World/World.fs +++ b/Nu/Nu/World/World.fs @@ -507,7 +507,7 @@ module WorldModule4 = // make the world's subsystems, loading initial packages where applicable let imGui = ImGui (false, windowViewport.Bounds.Size) - let physicsEngine2d = plugin.CreatePhysicsEngine2d () + let physicsEngine2d = plugin.MakePhysicsEngine2d () let physicsEngine3d = JoltPhysicsEngine.make Constants.Physics.GravityDefault let joltDebugRendererImGuiOpt = new JoltDebugRendererImGui () let rendererProcess = diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index d975ffd9f0..d4879a92cc 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -1232,7 +1232,7 @@ module WorldImGui2 = let segments = Dictionary () let circles = Dictionary () let physicsEngine2d = World.getPhysicsEngine2d world - let renderContext = World.createPhysicsEngine2dRenderContext segments circles world + let renderContext = World.makePhysicsEngine2dRenderContext segments circles world physicsEngine2d.TryRender renderContext for struct (color, segmentList) in segments.Pairs' do World.imGuiSegments2d false segmentList 1.0f color world diff --git a/Nu/Nu/World/WorldPhysics.fs b/Nu/Nu/World/WorldPhysics.fs index 00d8b26da1..abb1262c32 100644 --- a/Nu/Nu/World/WorldPhysics.fs +++ b/Nu/Nu/World/WorldPhysics.fs @@ -21,8 +21,8 @@ module WorldPhysics = static member internal getRendererPhysics3dOpt (world : World) = world.Subsystems.RendererPhysics3dOpt - static member internal createPhysicsEngine2dRenderContext segments circles (world : World) = - world.WorldExtension.Plugin.CreatePhysicsEngine2dRenderContext segments circles world.Eye2dBounds + static member internal makePhysicsEngine2dRenderContext segments circles (world : World) = + world.WorldExtension.Plugin.MakePhysicsEngine2dRenderContext segments circles world.Eye2dBounds /// Localize a primitive body shape to a specific size; non-primitive body shapes are unaffected. static member localizePrimitiveBodyShape (size : Vector3) (bodyShape : BodyShape) = diff --git a/Nu/Nu/World/WorldTypes.fs b/Nu/Nu/World/WorldTypes.fs index 52fbf1f3c5..0833688b30 100644 --- a/Nu/Nu/World/WorldTypes.fs +++ b/Nu/Nu/World/WorldTypes.fs @@ -2227,29 +2227,6 @@ and [] NuPlugin () = abstract InitialPackages : string list default this.InitialPackages = [] - /// Create the 2D physics engine for the engine to use. - abstract CreatePhysicsEngine2d : unit -> PhysicsEngine - default this.CreatePhysicsEngine2d () = - AetherPhysicsEngine.make (Constants.Physics.GravityDefault * Constants.Engine.Meter2d) - - /// Create the 2D physics engine renderer for the engine to use for the current frame. - abstract CreatePhysicsEngine2dRenderContext : - segments : Dictionary -> - circles : Dictionary -> - eyeBounds : Box2 -> - PhysicsEngineRenderContext - default this.CreatePhysicsEngine2dRenderContext segments circles eyeBounds = - { new AetherPhysicsEngineRenderContext with - override this.DrawLine (start : Vector2, stop : Vector2, color) = - match segments.TryGetValue color with - | (true, segmentList) -> segmentList.Add (start, stop) - | (false, _) -> segments.Add (color, List [struct (start, stop)]) - override this.DrawCircle (center : Vector2, radius, color) = - match circles.TryGetValue struct (color, radius) with - | (true, circleList) -> circleList.Add center - | (false, _) -> circles.Add (struct (color, radius), List [center]) - override _.EyeBounds = eyeBounds } - /// Clean-up any user-defined resources of the plugin, such with shutting down a Steamworks API. abstract CleanUp : unit -> unit default this.CleanUp () = () @@ -2270,6 +2247,29 @@ and [] NuPlugin () = | "BasicStaticBillboardEmitter" -> Particles.BasicStaticBillboardEmitter.makeDefault time lifeTimeOpt particleLifeTimeOpt particleRate particleMax :> Particles.Emitter |> Some | _ -> None + /// Make the 2D physics engine for the engine to use. + abstract MakePhysicsEngine2d : unit -> PhysicsEngine + default this.MakePhysicsEngine2d () = + AetherPhysicsEngine.make (Constants.Physics.GravityDefault * Constants.Engine.Meter2d) + + /// Make a 2D physics engine render context for the engine to use for the current frame. + abstract MakePhysicsEngine2dRenderContext : + segments : Dictionary -> + circles : Dictionary -> + eyeBounds : Box2 -> + PhysicsEngineRenderContext + default this.MakePhysicsEngine2dRenderContext segments circles eyeBounds = + { new AetherPhysicsEngineRenderContext with + override this.DrawLine (start : Vector2, stop : Vector2, color) = + match segments.TryGetValue color with + | (true, segmentList) -> segmentList.Add (start, stop) + | (false, _) -> segments.Add (color, List [struct (start, stop)]) + override this.DrawCircle (center : Vector2, radius, color) = + match circles.TryGetValue struct (color, radius) with + | (true, circleList) -> circleList.Add center + | (false, _) -> circles.Add (struct (color, radius), List [center]) + override _.EyeBounds = eyeBounds } + /// A call-back at the beginning of each frame. abstract PreProcess : world : World -> unit default this.PreProcess _ = () From 19f51849e11083165abdd7dc2200dac7f3cd8be4 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 6 Nov 2025 01:34:04 -0500 Subject: [PATCH 12/52] Moved makePhysicsEngine2dRenderContext to appropriate place. --- Nu/Nu/World/WorldModule.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Nu/Nu/World/WorldModule.fs b/Nu/Nu/World/WorldModule.fs index 45d0e66138..16a2d12dae 100644 --- a/Nu/Nu/World/WorldModule.fs +++ b/Nu/Nu/World/WorldModule.fs @@ -917,6 +917,9 @@ module WorldModule = static member tryMakeEmitter time lifeTimeOpt particleLifeTimeMaxOpt particleRate particleMax emitterStyle (world : World) = world.WorldExtension.Plugin.TryMakeEmitter time lifeTimeOpt particleLifeTimeMaxOpt particleRate particleMax emitterStyle + static member internal makePhysicsEngine2dRenderContext segments circles (world : World) = + world.WorldExtension.Plugin.MakePhysicsEngine2dRenderContext segments circles world.Eye2dBounds + static member internal preProcess (world : World) = world.WorldExtension.Plugin.PreProcess world From bb23d79f1c0ecb8241b8d37046a6d1d8b0ef868a Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 6 Nov 2025 01:38:41 -0500 Subject: [PATCH 13/52] Compile fix. --- Nu/Nu/World/WorldPhysics.fs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Nu/Nu/World/WorldPhysics.fs b/Nu/Nu/World/WorldPhysics.fs index abb1262c32..77f212aa5b 100644 --- a/Nu/Nu/World/WorldPhysics.fs +++ b/Nu/Nu/World/WorldPhysics.fs @@ -21,9 +21,6 @@ module WorldPhysics = static member internal getRendererPhysics3dOpt (world : World) = world.Subsystems.RendererPhysics3dOpt - static member internal makePhysicsEngine2dRenderContext segments circles (world : World) = - world.WorldExtension.Plugin.MakePhysicsEngine2dRenderContext segments circles world.Eye2dBounds - /// Localize a primitive body shape to a specific size; non-primitive body shapes are unaffected. static member localizePrimitiveBodyShape (size : Vector3) (bodyShape : BodyShape) = Physics.localizePrimitiveBodyShape size bodyShape From 8ca4987bd21bd359e81506f9be1cc2604977b951 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 6 Nov 2025 12:18:05 -0500 Subject: [PATCH 14/52] Formatting fixes. --- Projects/Sand Box 2d/ToyBox.fs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Projects/Sand Box 2d/ToyBox.fs b/Projects/Sand Box 2d/ToyBox.fs index 0926169b0b..ca17debb54 100644 --- a/Projects/Sand Box 2d/ToyBox.fs +++ b/Projects/Sand Box 2d/ToyBox.fs @@ -505,8 +505,8 @@ type ToyBoxDispatcher () = [Entity.BodyJoint |= twoBodyBodyJoint Entity.BodyJointTarget .= Address.makeFromString $"^/{n1}" Entity.BodyJointTarget2 .= Address.makeFromString $"^/{n2}" - Entity.CollideConnected .= true // Each box linked should collide with each other - ] world |> ignore + Entity.CollideConnected .= true] // each box linked should collide with each other + world |> ignore // declare distance joint linkage between contour boxes and center ball for stabilizing the shape for n in boxNames do @@ -520,8 +520,7 @@ type ToyBoxDispatcher () = // specified by starting with the parent link denoted by "^", then accessing the sub-entity using "/". [Entity.BodyJoint |= twoBodyJoint Entity.BodyJointTarget .= center.EntityAddress - Entity.BodyJointTarget2 .= Address.makeFromString $"^/{n}" - ] world |> ignore + Entity.BodyJointTarget2 .= Address.makeFromString $"^/{n}"] world |> ignore // end center ball declaration World.endEntity world From 8b58acd618c45d3c49aff0838b36b9160a47bae4 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Fri, 7 Nov 2025 04:44:25 +0800 Subject: [PATCH 15/52] Introduce Box2dNetFixPenetrationMessageFrame flag for fixing penetration message frame --- Nu/Nu/Core/Constants.fs | 1 + Nu/Nu/Physics/AetherPhysicsEngine.fs | 9 +- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 178 +++++++++++++------------ 3 files changed, 100 insertions(+), 88 deletions(-) diff --git a/Nu/Nu/Core/Constants.fs b/Nu/Nu/Core/Constants.fs index 7b023daefd..1f68643ef8 100644 --- a/Nu/Nu/Core/Constants.fs +++ b/Nu/Nu/Core/Constants.fs @@ -343,6 +343,7 @@ module Physics = let [] FrictionDefault = 0.5f let [] AngularDampingDefault = 0.2f let [] CollisionWildcard = "*" + let [] mutable Box2dNetFixPenetrationMessageFrame = match ConfigurationManager.AppSettings.["Box2dNetFixPenetrationMessageFrame"] with null -> false | value -> scvalue value let [] mutable Collision2dSteps = match ConfigurationManager.AppSettings.["Collision2dSteps"] with null -> 4 | value -> scvalue value let [] mutable Collision3dBodiesMax = match ConfigurationManager.AppSettings.["Collision3dBodiesMax"] with null -> 65536 | value -> scvalue value let [] mutable Collision3dBodyPairsMax = match ConfigurationManager.AppSettings.["Collision3dBodyPairsMax"] with null -> 32768 | value -> scvalue value diff --git a/Nu/Nu/Physics/AetherPhysicsEngine.fs b/Nu/Nu/Physics/AetherPhysicsEngine.fs index ad1a9948dd..2d4ab9b6a0 100644 --- a/Nu/Nu/Physics/AetherPhysicsEngine.fs +++ b/Nu/Nu/Physics/AetherPhysicsEngine.fs @@ -605,17 +605,20 @@ and [] AetherPhysicsEngine = Array.ofSeq contacts static member private configureBodyShapeProperties bodyProperties bodyShapePropertiesOpt (bodyShape : Fixture) = - // NOTE: temporary uint64 -> int conversions before we convert Aether to Box2D. + // NOTE: compared to Box2D.NET (Box2D v3), Aether.Physics2D (Box2D v2) only supports int16 instead of int for collision group, + // and int instead of uint64 for collision categories / mask. match bodyShapePropertiesOpt with | Some bodyShapeProperties -> bodyShape.Friction <- match bodyShapeProperties.FrictionOpt with Some f -> f | None -> bodyProperties.Friction bodyShape.Restitution <- match bodyShapeProperties.RestitutionOpt with Some r -> r | None -> bodyProperties.Restitution + bodyShape.CollisionGroup <- int16 <| match bodyShapeProperties.CollisionGroupOpt with Some cg -> cg | None -> bodyProperties.CollisionGroup bodyShape.CollisionCategories <- match bodyShapeProperties.CollisionCategoriesOpt with Some cc -> enum (int cc) | None -> enum (int bodyProperties.CollisionCategories) bodyShape.CollidesWith <- match bodyShapeProperties.CollisionMaskOpt with Some cm -> enum (int cm) | None -> enum (int bodyProperties.CollisionMask) bodyShape.IsSensor <- match bodyShapeProperties.SensorOpt with Some sensor -> sensor | None -> bodyProperties.Sensor | None -> bodyShape.Friction <- bodyProperties.Friction bodyShape.Restitution <- bodyProperties.Restitution + bodyShape.CollisionGroup <- int16 bodyProperties.CollisionGroup bodyShape.CollisionCategories <- enum (int bodyProperties.CollisionCategories) bodyShape.CollidesWith <- enum (int bodyProperties.CollisionMask) bodyShape.IsSensor <- bodyProperties.Sensor @@ -1353,7 +1356,6 @@ and [] AetherPhysicsEngine = | (false, _) -> 0.0f member physicsEngine.RayCast (ray, collisionCategory, collisionMask, closestOnly) = - ignore collisionCategory // not supported yet let results = List () let mutable fractionMin = Single.MaxValue let mutable closestOpt = None @@ -1361,7 +1363,8 @@ and [] AetherPhysicsEngine = RayCastReportFixtureDelegate (fun fixture point normal fraction -> match fixture.Tag with | :? BodyShapeIndex as bodyShapeIndex -> - if (int fixture.CollidesWith &&& int collisionMask) <> 0 then + if (int fixture.CollisionCategories &&& int collisionMask) <> 0 && + (int fixture.CollidesWith &&& int collisionCategory) <> 0 then let report = BodyIntersection.make bodyShapeIndex fraction (AetherPhysicsEngine.toPixelV3 point) (v3 normal.X normal.Y 0.0f) if fraction < fractionMin then fractionMin <- fraction diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index 6bc343a59c..e35fe00d91 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -518,8 +518,7 @@ type private Box2dNetFluidEmitter = type private Box2dNetPhysicsEngineContactsTracker = { NewContacts : ConcurrentDictionary - ExistingContacts : HashSet - } + ExistingContacts : HashSet } /// The Box2D.NET interface of PhysicsEngineRenderContext. type Box2dNetPhysicsEngineRenderContext = @@ -700,8 +699,8 @@ type [] Box2dNetPhysicsEngine = bodyShapeDef.filter.categoryBits <- bodyProperties.CollisionCategories bodyShapeDef.filter.maskBits <- bodyProperties.CollisionMask bodyShapeDef.isSensor <- bodyProperties.Sensor - bodyShapeDef.enablePreSolveEvents <- true // record non-sensor begin contact events via preSolveCallback - bodyShapeDef.enableContactEvents <- true // record non-sensor end contacts via b2World_GetContactEvents + bodyShapeDef.enablePreSolveEvents <- Constants.Physics.Box2dNetFixPenetrationMessageFrame // record non-sensor begin contact events via preSolveCallback only when needed + bodyShapeDef.enableContactEvents <- true // record non-sensor contacts via b2World_GetContactEvents bodyShapeDef.enableSensorEvents <- true // record sensor contacts via b2World_GetSensorEvents bodyShapeDef.userData <- { BodyId = { BodySource = bodySource; BodyIndex = bodyProperties.BodyIndex } @@ -1204,8 +1203,7 @@ type [] Box2dNetPhysicsEngine = | B2JointType.b2_wheelJoint -> B2WheelJoints.b2WheelJoint_EnableMotor (joint, setBodyJointMotorEnabledMessage.MotorEnabled) | _ -> () if setBodyJointMotorEnabledMessage.MotorEnabled then - B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyA joint, true) // force bodies awake - B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyB joint, true) + B2Joints.b2Joint_WakeBodies joint | (false, _) -> () static member private setBodyJointMotorSpeed (setBodyJointMotorSpeedMessage : SetBodyJointMotorSpeedMessage) physicsEngine = @@ -1218,8 +1216,7 @@ type [] Box2dNetPhysicsEngine = | B2JointType.b2_wheelJoint -> B2WheelJoints.b2WheelJoint_SetMotorSpeed (joint, setBodyJointMotorSpeedMessage.MotorSpeed) | _ -> () if setBodyJointMotorSpeedMessage.MotorSpeed <> 0.0f then - B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyA joint, true) // force bodies awake - B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyB joint, true) + B2Joints.b2Joint_WakeBodies joint | (false, _) -> () static member private setBodyJointTargetAngle (setBodyJointTargetAngleMessage : SetBodyJointTargetAngleMessage) physicsEngine = @@ -1228,9 +1225,8 @@ type [] Box2dNetPhysicsEngine = match B2Joints.b2Joint_GetType joint with | B2JointType.b2_revoluteJoint -> B2RevoluteJoints.b2RevoluteJoint_SetTargetAngle (joint, setBodyJointTargetAngleMessage.TargetAngle) - B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyA joint, true) // force bodies awake - B2Bodies.b2Body_SetAwake (B2Joints.b2Joint_GetBodyB joint, true) | _ -> () + B2Joints.b2Joint_WakeBodies joint | (false, _) -> () static member private applyBodyLinearImpulse (applyBodyLinearImpulseMessage : ApplyBodyLinearImpulseMessage) physicsEngine = @@ -1463,6 +1459,74 @@ type [] Box2dNetPhysicsEngine = ContactsTracker = contactsTracker IntegrationMessages = List () } :> PhysicsEngine + static let renderCallback = + b2OverlapResultFcn (fun shape context -> + let renderContext = context :?> Box2dNetPhysicsEngineRenderContext + + // get body and transform + let body = B2Shapes.b2Shape_GetBody shape + let mutable transform = B2Bodies.b2Body_GetTransform body + + // compute color consistent with JoltSharp which defaults to MotionTypeColor: https://github.com/amerkoleci/JoltPhysicsSharp/blob/fbc0511c987043a16b6f985ae00633285ee56cb9/src/JoltPhysicsSharp/DrawSettings.cs#L33 + // which is defined here: https://github.com/amerkoleci/JoltPhysicsSharp/blob/fbc0511c987043a16b6f985ae00633285ee56cb9/src/JoltPhysicsSharp/ShapeColor.cs#L20 + let color = + match B2Bodies.b2Body_GetType body with + | B2BodyType.b2_dynamicBody -> // dynamic = random color per instance + (B2Bodies.b2Body_GetUserData body).GetHashCode () |> uint |> colorPacked |> _.WithA(1f) // use the Nu BodyIndex because physics engine bodies are recreated on property assignment + | B2BodyType.b2_kinematicBody -> // keyframed + Color.Green + | _ -> // static or anything else + Color.Gray + + // render shape + match B2Shapes.b2Shape_GetType shape with + | B2ShapeType.b2_circleShape -> + let circle = B2Shapes.b2Shape_GetCircle shape + let position = B2MathFunction.b2TransformPoint (&transform, circle.center) |> Box2dNetPhysicsEngine.toPixelV2 + let radius = Box2dNetPhysicsEngine.toPixel circle.radius + renderContext.DrawCircle (position, radius, color) + | B2ShapeType.b2_capsuleShape -> + let capsule = B2Shapes.b2Shape_GetCapsule shape + let center1 = B2MathFunction.b2TransformPoint (&transform, capsule.center1) |> Box2dNetPhysicsEngine.toPixelV2 + let center2 = B2MathFunction.b2TransformPoint (&transform, capsule.center2) |> Box2dNetPhysicsEngine.toPixelV2 + let radius = Box2dNetPhysicsEngine.toPixel capsule.radius + let direction = center2 - center1 + let perpendicular = Vector2(-direction.Y, direction.X).Normalized * radius + renderContext.DrawCircle (center1, radius, color) + renderContext.DrawCircle (center2, radius, color) + renderContext.DrawLine (center1 + perpendicular, center2 + perpendicular, color) + renderContext.DrawLine (center1 - perpendicular, center2 - perpendicular, color) + | B2ShapeType.b2_segmentShape -> + let segment = B2Shapes.b2Shape_GetSegment shape + let start = B2MathFunction.b2TransformPoint (&transform, segment.point1) |> Box2dNetPhysicsEngine.toPixelV2 + let stop = B2MathFunction.b2TransformPoint (&transform, segment.point2) |> Box2dNetPhysicsEngine.toPixelV2 + renderContext.DrawLine (start, stop, color) + | B2ShapeType.b2_polygonShape -> + let polygon = B2Shapes.b2Shape_GetPolygon shape + if polygon.radius = 0.0f then + for i in 0 .. dec polygon.count do + let start = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[i]) |> Box2dNetPhysicsEngine.toPixelV2 + let stop = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[if i < dec polygon.count then inc i else 0]) |> Box2dNetPhysicsEngine.toPixelV2 + renderContext.DrawLine (start, stop, color) + else + let radius = Box2dNetPhysicsEngine.toPixel polygon.radius + for i in 0 .. dec polygon.count do + let start = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[i]) |> Box2dNetPhysicsEngine.toPixelV2 + let stop = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[if i < dec polygon.count then inc i else 0]) |> Box2dNetPhysicsEngine.toPixelV2 + let perpendicular = B2MathFunction.b2RotateVector (transform.q, polygon.normals[i]) * radius + let perpendicular = v2 perpendicular.X perpendicular.Y + renderContext.DrawCircle (start, radius, color) + renderContext.DrawLine (start + perpendicular, stop + perpendicular, color) + | B2ShapeType.b2_chainSegmentShape -> + let segment = (B2Shapes.b2Shape_GetChainSegment shape).segment + let start = B2MathFunction.b2TransformPoint (&transform, segment.point1) |> Box2dNetPhysicsEngine.toPixelV2 + let stop = B2MathFunction.b2TransformPoint (&transform, segment.point2) |> Box2dNetPhysicsEngine.toPixelV2 + renderContext.DrawLine (start, stop, color) + | _ -> () + + // continue querying + true) + interface PhysicsEngine with member physicsEngine.GravityDefault = @@ -1646,15 +1710,25 @@ type [] Box2dNetPhysicsEngine = LinearVelocity = Box2dNetPhysicsEngine.toPixelV3 (B2Bodies.b2Body_GetLinearVelocity transform.bodyId) AngularVelocity = v3 0.0f 0.0f (B2Bodies.b2Body_GetAngularVelocity transform.bodyId) }) - // collect penetrations for non-sensors from preSolveCallback which exist on same time step as penetration unlike contact events which exist one step later. - for KeyValue (shapeIds, penetration) in physicsEngine.ContactsTracker.NewContacts do - physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeSource; BodyShapeTarget = penetration.BodyShapeTarget; Normal = penetration.Normal }) - physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeTarget; BodyShapeTarget = penetration.BodyShapeSource; Normal = -penetration.Normal }) - physicsEngine.ContactsTracker.ExistingContacts.Add shapeIds |> ignore - physicsEngine.ContactsTracker.NewContacts.Clear () + let contacts = B2Worlds.b2World_GetContactEvents physicsEngine.PhysicsContextId + if Constants.Physics.Box2dNetFixPenetrationMessageFrame then + // collect penetrations for non-sensors from preSolveCallback which is called on same time step as penetration unlike contact events which exist one step later. + for KeyValue (shapeIds, penetration) in physicsEngine.ContactsTracker.NewContacts do + physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeSource; BodyShapeTarget = penetration.BodyShapeTarget; Normal = penetration.Normal }) + physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeTarget; BodyShapeTarget = penetration.BodyShapeSource; Normal = -penetration.Normal }) + physicsEngine.ContactsTracker.ExistingContacts.Add shapeIds |> ignore + physicsEngine.ContactsTracker.NewContacts.Clear () + else + // collect penetrations for non-sensors from begin contact events, which are performant but one time step late compared to the actual penetration. + for i in 0 .. dec contacts.beginCount do + let penetration = &contacts.beginEvents.[i] + let bodyShapeA = B2Shapes.b2Shape_GetUserData penetration.shapeIdA :?> BodyShapeIndex + let bodyShapeB = B2Shapes.b2Shape_GetUserData penetration.shapeIdB :?> BodyShapeIndex + let normal = v3 penetration.manifold.normal.X penetration.manifold.normal.Y 0.0f + physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = bodyShapeA; BodyShapeTarget = bodyShapeB; Normal = normal }) + physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = bodyShapeB; BodyShapeTarget = bodyShapeA; Normal = -normal }) // collect separations for non-sensors - let contacts = B2Worlds.b2World_GetContactEvents physicsEngine.PhysicsContextId for i in 0 .. dec contacts.endCount do let separation = &contacts.endEvents.[i] physicsEngine.ContactsTracker.ExistingContacts.Remove (separation.shapeIdA, separation.shapeIdB) |> ignore @@ -1710,79 +1784,13 @@ type [] Box2dNetPhysicsEngine = let eyeBounds = renderContext.EyeBounds let v2ToB2Vec2 (v : Vector2) = B2Vec2 (Box2dNetPhysicsEngine.toPhysics v.X, Box2dNetPhysicsEngine.toPhysics v.Y) let eyeAabb = B2AABB (v2ToB2Vec2 eyeBounds.Min, v2ToB2Vec2 eyeBounds.Max) - let callback = - b2OverlapResultFcn (fun shape _ -> - - // get body and transform - let body = B2Shapes.b2Shape_GetBody shape - let mutable transform = B2Bodies.b2Body_GetTransform body - - // compute color consistent with JoltSharp which defaults to MotionTypeColor: https://github.com/amerkoleci/JoltPhysicsSharp/blob/fbc0511c987043a16b6f985ae00633285ee56cb9/src/JoltPhysicsSharp/DrawSettings.cs#L33 - // which is defined here: https://github.com/amerkoleci/JoltPhysicsSharp/blob/fbc0511c987043a16b6f985ae00633285ee56cb9/src/JoltPhysicsSharp/ShapeColor.cs#L20 - let color = - match B2Bodies.b2Body_GetType body with - | B2BodyType.b2_dynamicBody -> // dynamic = random color per instance - (B2Bodies.b2Body_GetUserData body).GetHashCode () |> uint |> colorPacked |> _.WithA(1f) // use the Nu BodyIndex because physics engine bodies are recreated on property assignment - | B2BodyType.b2_kinematicBody -> // keyframed - Color.Green - | _ -> // static or anything else - Color.Gray - - // render shape - match B2Shapes.b2Shape_GetType shape with - | B2ShapeType.b2_circleShape -> - let circle = B2Shapes.b2Shape_GetCircle shape - let position = B2MathFunction.b2TransformPoint (&transform, circle.center) |> Box2dNetPhysicsEngine.toPixelV2 - let radius = Box2dNetPhysicsEngine.toPixel circle.radius - renderContext.DrawCircle (position, radius, color) - | B2ShapeType.b2_capsuleShape -> - let capsule = B2Shapes.b2Shape_GetCapsule shape - let center1 = B2MathFunction.b2TransformPoint (&transform, capsule.center1) |> Box2dNetPhysicsEngine.toPixelV2 - let center2 = B2MathFunction.b2TransformPoint (&transform, capsule.center2) |> Box2dNetPhysicsEngine.toPixelV2 - let radius = Box2dNetPhysicsEngine.toPixel capsule.radius - let direction = center2 - center1 - let perpendicular = Vector2(-direction.Y, direction.X).Normalized * radius - renderContext.DrawCircle (center1, radius, color) - renderContext.DrawCircle (center2, radius, color) - renderContext.DrawLine (center1 + perpendicular, center2 + perpendicular, color) - renderContext.DrawLine (center1 - perpendicular, center2 - perpendicular, color) - | B2ShapeType.b2_segmentShape -> - let segment = B2Shapes.b2Shape_GetSegment shape - let start = B2MathFunction.b2TransformPoint (&transform, segment.point1) |> Box2dNetPhysicsEngine.toPixelV2 - let stop = B2MathFunction.b2TransformPoint (&transform, segment.point2) |> Box2dNetPhysicsEngine.toPixelV2 - renderContext.DrawLine (start, stop, color) - | B2ShapeType.b2_polygonShape -> - let polygon = B2Shapes.b2Shape_GetPolygon shape - if polygon.radius = 0.0f then - for i in 0 .. dec polygon.count do - let start = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[i]) |> Box2dNetPhysicsEngine.toPixelV2 - let stop = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[if i < dec polygon.count then inc i else 0]) |> Box2dNetPhysicsEngine.toPixelV2 - renderContext.DrawLine (start, stop, color) - else - let radius = Box2dNetPhysicsEngine.toPixel polygon.radius - for i in 0 .. dec polygon.count do - let start = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[i]) |> Box2dNetPhysicsEngine.toPixelV2 - let stop = B2MathFunction.b2TransformPoint (&transform, polygon.vertices.[if i < dec polygon.count then inc i else 0]) |> Box2dNetPhysicsEngine.toPixelV2 - let perpendicular = B2MathFunction.b2RotateVector (transform.q, polygon.normals[i]) * radius - let perpendicular = v2 perpendicular.X perpendicular.Y - renderContext.DrawCircle (start, radius, color) - renderContext.DrawLine (start + perpendicular, stop + perpendicular, color) - | B2ShapeType.b2_chainSegmentShape -> - let segment = (B2Shapes.b2Shape_GetChainSegment shape).segment - let start = B2MathFunction.b2TransformPoint (&transform, segment.point1) |> Box2dNetPhysicsEngine.toPixelV2 - let stop = B2MathFunction.b2TransformPoint (&transform, segment.point2) |> Box2dNetPhysicsEngine.toPixelV2 - renderContext.DrawLine (start, stop, color) - | _ -> () - - // continue querying - true) B2Worlds.b2World_OverlapAABB (physicsEngine.PhysicsContextId, eyeAabb, B2QueryFilter (UInt64.MaxValue, UInt64.MaxValue), - callback, - null) |> ignore + renderCallback, + (renderContext : Box2dNetPhysicsEngineRenderContext)) |> ignore | _ -> () From a5cb3f1a1dbf00c485449af5720a5084ae95e8f9 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Fri, 7 Nov 2025 04:46:58 +0800 Subject: [PATCH 16/52] undo name change --- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index e35fe00d91..ad9630fdcf 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -95,8 +95,8 @@ type private Box2dNetFluidEmitter = static member positionToCellId cellSize (position : Vector2) = v2i (floor (position.X / cellSize) |> int) (floor (position.Y / cellSize) |> int) - static member cellIdToBox cellSize (cell : Vector2i) = - box2 (cell.V2 * cellSize) (v2Dup cellSize) + static member cellIdToBox cellSize (cellId : Vector2i) = + box2 (cellId.V2 * cellSize) (v2Dup cellSize) static member updateDescriptor (descriptor : FluidEmitterDescriptor2d) (fluidEmitter : Box2dNetFluidEmitter) = if not descriptor.Enabled then From cb7be193e9818728a8dbfc155cd269ad41943841 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 6 Nov 2025 18:33:31 -0500 Subject: [PATCH 17/52] PR fixes. --- Nu/Nu/Core/Constants.fs | 2 +- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 15 +++++++++------ Nu/Nu/World/WorldConfigure.fs | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Nu/Nu/Core/Constants.fs b/Nu/Nu/Core/Constants.fs index 1f68643ef8..cb056ecd3f 100644 --- a/Nu/Nu/Core/Constants.fs +++ b/Nu/Nu/Core/Constants.fs @@ -343,8 +343,8 @@ module Physics = let [] FrictionDefault = 0.5f let [] AngularDampingDefault = 0.2f let [] CollisionWildcard = "*" - let [] mutable Box2dNetFixPenetrationMessageFrame = match ConfigurationManager.AppSettings.["Box2dNetFixPenetrationMessageFrame"] with null -> false | value -> scvalue value let [] mutable Collision2dSteps = match ConfigurationManager.AppSettings.["Collision2dSteps"] with null -> 4 | value -> scvalue value + let [] mutable Collision2dFrameCompensation = match ConfigurationManager.AppSettings.["Collision2dFrameCompensation"] with null -> false | value -> scvalue value let [] mutable Collision3dBodiesMax = match ConfigurationManager.AppSettings.["Collision3dBodiesMax"] with null -> 65536 | value -> scvalue value let [] mutable Collision3dBodyPairsMax = match ConfigurationManager.AppSettings.["Collision3dBodyPairsMax"] with null -> 32768 | value -> scvalue value let [] mutable Collision3dContactConstraintsMax = match ConfigurationManager.AppSettings.["Collision3dContactConstraintsMax"] with null -> 16384 | value -> scvalue value diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index ad9630fdcf..5b545d5500 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -538,8 +538,7 @@ type [] Box2dNetPhysicsEngine = CreateBodyJointMessages : Dictionary FluidEmitters : Dictionary IntegrationMessages : IntegrationMessage List // OPTIMIZATION: cached to avoid large arrays filling up the LOH. - ContactsTracker : Box2dNetPhysicsEngineContactsTracker // NOTE: supports thread safety for b2PreSolveFcn. - } + ContactsTracker : Box2dNetPhysicsEngineContactsTracker } // NOTE: supports thread safety for b2PreSolveFcn. member private this.PhysicsContext = B2Worlds.b2GetWorld (int this.PhysicsContextId.index1) @@ -699,7 +698,7 @@ type [] Box2dNetPhysicsEngine = bodyShapeDef.filter.categoryBits <- bodyProperties.CollisionCategories bodyShapeDef.filter.maskBits <- bodyProperties.CollisionMask bodyShapeDef.isSensor <- bodyProperties.Sensor - bodyShapeDef.enablePreSolveEvents <- Constants.Physics.Box2dNetFixPenetrationMessageFrame // record non-sensor begin contact events via preSolveCallback only when needed + bodyShapeDef.enablePreSolveEvents <- Constants.Physics.Collision2dFrameCompensation // record non-sensor begin contact events via preSolveCallback only when needed bodyShapeDef.enableContactEvents <- true // record non-sensor contacts via b2World_GetContactEvents bodyShapeDef.enableSensorEvents <- true // record sensor contacts via b2World_GetSensorEvents bodyShapeDef.userData <- @@ -1710,16 +1709,20 @@ type [] Box2dNetPhysicsEngine = LinearVelocity = Box2dNetPhysicsEngine.toPixelV3 (B2Bodies.b2Body_GetLinearVelocity transform.bodyId) AngularVelocity = v3 0.0f 0.0f (B2Bodies.b2Body_GetAngularVelocity transform.bodyId) }) + // collect penetrations let contacts = B2Worlds.b2World_GetContactEvents physicsEngine.PhysicsContextId - if Constants.Physics.Box2dNetFixPenetrationMessageFrame then - // collect penetrations for non-sensors from preSolveCallback which is called on same time step as penetration unlike contact events which exist one step later. + if Constants.Physics.Collision2dFrameCompensation then + + // collect penetrations for non-sensors from preSolveCallback which is called on same time step as penetration unlike contact events which exist one step later for KeyValue (shapeIds, penetration) in physicsEngine.ContactsTracker.NewContacts do physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeSource; BodyShapeTarget = penetration.BodyShapeTarget; Normal = penetration.Normal }) physicsEngine.IntegrationMessages.Add (BodyPenetrationMessage { BodyShapeSource = penetration.BodyShapeTarget; BodyShapeTarget = penetration.BodyShapeSource; Normal = -penetration.Normal }) physicsEngine.ContactsTracker.ExistingContacts.Add shapeIds |> ignore physicsEngine.ContactsTracker.NewContacts.Clear () + else - // collect penetrations for non-sensors from begin contact events, which are performant but one time step late compared to the actual penetration. + + // collect penetrations for non-sensors from begin contact events, which are performant but one time step late compared to the actual penetration for i in 0 .. dec contacts.beginCount do let penetration = &contacts.beginEvents.[i] let bodyShapeA = B2Shapes.b2Shape_GetUserData penetration.shapeIdA :?> BodyShapeIndex diff --git a/Nu/Nu/World/WorldConfigure.fs b/Nu/Nu/World/WorldConfigure.fs index e8109f2916..eeb1d57568 100644 --- a/Nu/Nu/World/WorldConfigure.fs +++ b/Nu/Nu/World/WorldConfigure.fs @@ -51,6 +51,7 @@ module Configure = | nameof Constants.Render.ShadowCascadeMarginRatio -> Constants.Render.ShadowCascadeMarginRatio <- scvalue value | nameof Constants.Render.ShadowCascadeMarginRatioCull -> Constants.Render.ShadowCascadeMarginRatioCull <- scvalue value | nameof Constants.Physics.Collision2dSteps -> Constants.Physics.Collision2dSteps <- scvalue value + | nameof Constants.Physics.Collision2dFrameCompensation -> Constants.Physics.Collision2dFrameCompensation <- scvalue value | nameof Constants.Physics.Collision3dBodiesMax -> Constants.Physics.Collision3dBodiesMax <- scvalue value | nameof Constants.Physics.Collision3dBodyPairsMax -> Constants.Physics.Collision3dBodyPairsMax <- scvalue value | nameof Constants.Physics.Collision3dContactConstraintsMax -> Constants.Physics.Collision3dContactConstraintsMax <- scvalue value From b37f8f7faee602788d64aaa9920ff4077067aff0 Mon Sep 17 00:00:00 2001 From: Bryan Edds Date: Fri, 7 Nov 2025 09:05:04 -0500 Subject: [PATCH 18/52] Update ReadMe.md --- ReadMe.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index f9908a3574..67c1bc94d8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -96,8 +96,6 @@ Recursive Prefab-like functionality via Entity Structure Propagation. **[@haraldsteinlechner](https://github.com/haraldsteinlechner)** -**[@ShalokShalom](https://github.com/ShalokShalom)** - **And a huge thank you to all of you who donate privately to keep this project going!** Please Become a Sponsor for Nu Today! From f3035492fafefc83e8b2a06286b369e47b6c2234 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 8 Nov 2025 12:25:27 -0500 Subject: [PATCH 19/52] Removed unnecessary qualifiers. --- Nu/Nu.Gaia/GaiaConstants.fs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Nu/Nu.Gaia/GaiaConstants.fs b/Nu/Nu.Gaia/GaiaConstants.fs index a30e12f831..70b027b3a2 100644 --- a/Nu/Nu.Gaia/GaiaConstants.fs +++ b/Nu/Nu.Gaia/GaiaConstants.fs @@ -12,7 +12,7 @@ module Constants = [] module Gaia = - let [] LogCharactersMax = Constants.Runtime.LohSize / sizeof - 5000 // NOTE: small enough not to allocate on the LOH. + let [] LogCharactersMax = Constants.Runtime.LohSize / sizeof - 5000 // NOTE: small enough to not allocate on the LOH. let [] PositionSnap2dDefault = 8.0f let [] DegreesSnap2dDefault = 5.0f let [] ScaleSnap2dDefault = 0.1f @@ -31,15 +31,15 @@ module Constants = let [] InteractiveInputFilePath = "input.fsx" let [] NonePick = "\"None\"" let [] EventFilter = - EventFilter.NotAny - [EventFilter.Pattern (Rexpr "PreUpdate", []) - EventFilter.Pattern (Rexpr "Update", []) - EventFilter.Pattern (Rexpr "PostUpdate", []) - EventFilter.Pattern (Rexpr "Render", []) - EventFilter.Pattern (Rexpr "Change", []) - EventFilter.Pattern (Rexpr "Integration", []) - EventFilter.Pattern (Rexpr "BodyTransform", []) - EventFilter.Pattern (Rexpr "Mouse/Move", [])] + NotAny + [Pattern (Rexpr "PreUpdate", []) + Pattern (Rexpr "Update", []) + Pattern (Rexpr "PostUpdate", []) + Pattern (Rexpr "Render", []) + Pattern (Rexpr "Change", []) + Pattern (Rexpr "Integration", []) + Pattern (Rexpr "BodyTransform", []) + Pattern (Rexpr "Mouse/Move", [])] let [] BuildName = #if DEBUG "Debug" From c0a7aee53e0a5a14dbdee4a181022a820621e752 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 8 Nov 2025 14:51:20 -0500 Subject: [PATCH 20/52] Whitespace clean-up. --- Nu/Nu.Spine/Nu.Spine.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nu/Nu.Spine/Nu.Spine.csproj b/Nu/Nu.Spine/Nu.Spine.csproj index 2c0be899f2..bb8010075b 100644 --- a/Nu/Nu.Spine/Nu.Spine.csproj +++ b/Nu/Nu.Spine/Nu.Spine.csproj @@ -33,4 +33,4 @@ - + \ No newline at end of file From f9f82f4b084b31590854f79fc393743a5bdd0c1e Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 8 Nov 2025 17:05:46 -0500 Subject: [PATCH 21/52] Normalized Metrics entity counts. --- Projects/Metrics/Program.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Projects/Metrics/Program.fs b/Projects/Metrics/Program.fs index 6b94bcec83..6b252faebe 100644 --- a/Projects/Metrics/Program.fs +++ b/Projects/Metrics/Program.fs @@ -47,7 +47,7 @@ type MmccGameMessage = interface Message type MmccGameDispatcher () = - inherit GameDispatcher (Intss.init 82) // 6,724 entities + inherit GameDispatcher (Intss.init 100) // 10,000 entities override this.Definitions (_, _) = [Game.UpdateEvent => Inc] @@ -63,7 +63,7 @@ type MmccGameDispatcher () = [for (j, int) in ints.Ints.Pairs' do Content.entity (string j) [Entity.Presence == Omnipresent - Entity.Position == v3 (single i * 4.25f - 245.0f) (single j * 2.25f - 125.0f) -250.0f + Entity.Position == v3 (single i * 5.0f - 245.0f) (single j * 2.75f - 135.0f) -250.0f Entity.Scale := v3Dup (single (int % 10)) * 0.5f]] Content.group "Other" [] [Content.skyBox "SkyBox" [] @@ -78,10 +78,10 @@ type MyGameDispatcher () = #if IMSIM inherit GameDispatcherImSim () - static let Positions = // 7,500 entities + static let Positions = // 10,000 entities [|for i in 0 .. dec 50 do for j in 0 .. dec 50 do - for k in 0 .. dec 3 do + for k in 0 .. dec 4 do yield v3 (single i * 0.5f) (single j * 0.5f) (single k * 0.5f)|] override this.Process (_, world) = From c199ced252bd8d7be2db3c0cf1203e4d19d2f8f8 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 8 Nov 2025 19:16:57 -0500 Subject: [PATCH 22/52] Removed unnecessary rec specifier. --- Nu/Nu.Gaia/Gaia.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 332dd86405..54142b4aaf 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -4283,7 +4283,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= Log.error errorMsg Constants.Engine.ExitCodeFailure - let rec private runWithCleanUp gaiaState targetDir_ screen world = + let private runWithCleanUp gaiaState targetDir_ screen world = OpenProjectFilePath <- gaiaState.ProjectDllPath OpenProjectImperativeExecution <- gaiaState.ProjectImperativeExecution CloseProjectImperativeExecution <- gaiaState.ProjectImperativeExecution From e8924c5ffa4383cb7fb53e64fac9bf3e6fe2a039 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 8 Nov 2025 19:54:21 -0500 Subject: [PATCH 23/52] Removed another unnecessary rec. --- Nu/Nu.Gaia/Gaia.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 54142b4aaf..58ece8d5a2 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1021,7 +1021,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= true | Some _ | None -> false - let rec private propagateEntityStructure entity world = + let private propagateEntityStructure entity world = snapshot PropagateEntity world World.propagateEntityStructure entity world From 4a1292e0ceacdfbbaa5b83adfb457536356c93df Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sun, 9 Nov 2025 02:08:25 -0500 Subject: [PATCH 24/52] Scene loading code clean-up. --- Projects/Sand Box 2d/ToyBox.fs | 9 ++++----- Projects/Terra Firma/Gameplay.fs | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Projects/Sand Box 2d/ToyBox.fs b/Projects/Sand Box 2d/ToyBox.fs index ca17debb54..de5e8603b4 100644 --- a/Projects/Sand Box 2d/ToyBox.fs +++ b/Projects/Sand Box 2d/ToyBox.fs @@ -327,8 +327,8 @@ type ToyBoxDispatcher () = WeldJoint (a, b, a.Position, b.Position, true) } Entity.BodyJointTarget .= anchor.EntityAddress Entity.BodyJointTarget2 .= blade.EntityAddress - Entity.CollideConnected .= false // When the two blades are set to collide, the + shape would deform on drag - ] world |> ignore + Entity.CollideConnected .= false] // when the two blades are set to collide, the + shape would deform on drag + world |> ignore // end anchor blade declaration World.endEntity world @@ -360,8 +360,7 @@ type ToyBoxDispatcher () = Entity.BodyJointTarget .= Address.makeFromString $"^/{linkTo}" Entity.BodyJointTarget2 .= Address.makeFromString $"^/{newLeg}" Entity.CollideConnected .= false // rotation movement would be limited if the upper leg collides with center - Entity.MountOpt .= None] - world |> ignore + Entity.MountOpt .= None] world |> ignore let isExtended = world.ClockTime % 10f >= 5f let twoBodyJoint = AetherBodyJoint { CreateBodyJoint = fun _ _ a b -> @@ -622,7 +621,7 @@ type ToyBoxDispatcher () = // declare motor World.doBodyJoint2d $"{name} Motor" [Entity.BodyJoint |= AetherBodyJoint { CreateBodyJoint = fun _ _ a b -> - // specifying a motor for the revolute joint rotates the first body with a constant angular velocity. + // specifying a motor for the revolute joint rotates the first body with a constant angular velocity RevoluteJoint (a, b, b.Position, true, MotorEnabled = true, MotorSpeed = 2f, MaxMotorTorque = 400f) } Entity.BodyJointTarget .= wheel.EntityAddress Entity.BodyJointTarget2 .= chassis.EntityAddress diff --git a/Projects/Terra Firma/Gameplay.fs b/Projects/Terra Firma/Gameplay.fs index 84ba6203d4..7554aeb1e1 100644 --- a/Projects/Terra Firma/Gameplay.fs +++ b/Projects/Terra Firma/Gameplay.fs @@ -44,8 +44,10 @@ type GameplayDispatcher () = // begin scene declaration, processing nav sync at end of frame since optimized representations like // frozen entities won't have their nav info registered until then - World.beginGroupFromFile Simulants.GameplayScene.Name "Assets/Gameplay/Scene.nugroup" [] world - if selecting then World.defer (World.synchronizeNav3d false (Some "Assets/Gameplay/Scene.nav") screen) screen world + let sceneGroupFilePath = "Assets/Gameplay/Scene.nugroup" + let sceneNavFilePath = PathF.ChangeExtension (sceneGroupFilePath, ".nav") + World.beginGroupFromFile Simulants.GameplayScene.Name sceneGroupFilePath [] world + if selecting then World.defer (World.synchronizeNav3d false (Some sceneNavFilePath) screen) screen world // declare player World.doEntity Simulants.GameplayPlayer.Name From e740fa9b3aceae89062319cfaef86b9752354376 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sun, 9 Nov 2025 03:42:16 -0500 Subject: [PATCH 25/52] Rather random code clean-up. --- Nu/Nu/World/WorldEvents.fs | 1 - Nu/Nu/World/WorldTypes.fs | 8 ++++---- Projects/Blaze Vector ImSim/Gameplay.fs | 3 +-- Projects/Terra Firma/TerraFirma.fs | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Nu/Nu/World/WorldEvents.fs b/Nu/Nu/World/WorldEvents.fs index d2adba2614..2883b7b26c 100644 --- a/Nu/Nu/World/WorldEvents.fs +++ b/Nu/Nu/World/WorldEvents.fs @@ -4,7 +4,6 @@ namespace Nu open System open System.Numerics -open System.Collections.Concurrent open Prime /// The data for a life-cycle event. diff --git a/Nu/Nu/World/WorldTypes.fs b/Nu/Nu/World/WorldTypes.fs index 0833688b30..917109ce4f 100644 --- a/Nu/Nu/World/WorldTypes.fs +++ b/Nu/Nu/World/WorldTypes.fs @@ -27,10 +27,10 @@ module internal WorldTypes = let mutable internal EmptyEntityContent = Unchecked.defaultof // Debugging F# reach-arounds. - let mutable internal viewGame = fun (_ : obj) (_ : obj) -> Array.create 0 (String.Empty, obj ()) - let mutable internal viewScreen = fun (_ : obj) (_ : obj) -> Array.create 0 (String.Empty, obj ()) - let mutable internal viewGroup = fun (_ : obj) (_ : obj) -> Array.create 0 (String.Empty, obj ()) - let mutable internal viewEntity = fun (_ : obj) (_ : obj) -> Array.create 0 (String.Empty, obj ()) + let mutable internal viewGame = fun (_ : obj) (_ : obj) -> Array.empty + let mutable internal viewScreen = fun (_ : obj) (_ : obj) -> Array.empty + let mutable internal viewGroup = fun (_ : obj) (_ : obj) -> Array.empty + let mutable internal viewEntity = fun (_ : obj) (_ : obj) -> Array.empty // EventGraph F# reach-arounds. let mutable internal getSelectedScreenIdling : obj -> bool = Unchecked.defaultof<_> diff --git a/Projects/Blaze Vector ImSim/Gameplay.fs b/Projects/Blaze Vector ImSim/Gameplay.fs index 224171fd39..92f2d2ac79 100644 --- a/Projects/Blaze Vector ImSim/Gameplay.fs +++ b/Projects/Blaze Vector ImSim/Gameplay.fs @@ -69,8 +69,7 @@ type GameplayDispatcher () = // declare player World.doEntity "Player" [Entity.Position |= v3 -390.0f -50.0f 0.0f - Entity.Elevation .= 1.0f] - world + Entity.Elevation .= 1.0f] world let player = world.DeclaredEntity // process scoring diff --git a/Projects/Terra Firma/TerraFirma.fs b/Projects/Terra Firma/TerraFirma.fs index a1c6c8f7ce..73383cba06 100644 --- a/Projects/Terra Firma/TerraFirma.fs +++ b/Projects/Terra Firma/TerraFirma.fs @@ -55,7 +55,7 @@ type TerraFirmaDispatcher () = if FQueue.contains Deselecting results then Simulants.Gameplay.SetGameplayState Quit world if Simulants.Gameplay.GetSelected world && Simulants.Gameplay.GetGameplayState world = Quit && - world.Advancing then + world.Advancing then game.SetGameState Title world World.endScreen world From 8ed5049014acfedc2b1cfb339e9792308ef26f7f Mon Sep 17 00:00:00 2001 From: bryanedds Date: Mon, 10 Nov 2025 17:42:07 -0500 Subject: [PATCH 26/52] Typo fix. --- Nu/Nu/OpenGL/OpenGL.Texture.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nu/Nu/OpenGL/OpenGL.Texture.fs b/Nu/Nu/OpenGL/OpenGL.Texture.fs index 08ee05212a..3c412fbfd0 100644 --- a/Nu/Nu/OpenGL/OpenGL.Texture.fs +++ b/Nu/Nu/OpenGL/OpenGL.Texture.fs @@ -41,7 +41,7 @@ module Texture = name.EndsWith "_f" || name.EndsWith "Filtered" - /// Infer the type of block compressionthat an asset with the given file path should utilize. + /// Infer the type of block compression that an asset with the given file path should utilize. let InferCompression (filePath : string) = let name = PathF.GetFileNameWithoutExtension filePath if name.EndsWith "_hm" || From bb6a5d58fa280f8379afb13f01e19bf16dc1ae50 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Tue, 11 Nov 2025 23:53:06 -0500 Subject: [PATCH 27/52] Fixed #1208. --- Nu/Nu.Spine/src/PhysicsConstraint.cs | 106 ++++++++++++------------- Nu/Nu.Spine/src/Skeleton.cs | 5 +- Nu/Nu/Assimp/Assimp.fs | 57 ++++++------- Nu/Nu/Audio/AudioPlayer.fs | 6 +- Nu/Nu/Core/Behavior.fs | 2 +- Nu/Nu/Core/Constants.fs | 6 +- Nu/Nu/Core/GameTime.fs | 50 +++++++----- Nu/Nu/Effects/EffectSystem.fs | 18 ++--- Nu/Nu/Particles/Particles.fs | 29 +++---- Nu/Nu/Physics/AetherPhysicsEngine.fs | 8 +- Nu/Nu/Physics/Box2dNetPhysicsEngine.fs | 4 +- Nu/Nu/Physics/JoltPhysicsEngine.fs | 11 +-- Nu/Nu/Physics/PhysicsEngine.fs | 2 +- Nu/Nu/World/WorldConstants.fs | 6 +- Nu/Nu/World/WorldDispatchers.fs | 2 +- Nu/Nu/World/WorldFacets.fs | 36 +++++---- Nu/Nu/World/WorldScreen.fs | 4 +- Projects/Sand Box 2d/FluidSim.fs | 2 +- Projects/Terra Firma/Character.fs | 2 +- 19 files changed, 186 insertions(+), 170 deletions(-) diff --git a/Nu/Nu.Spine/src/PhysicsConstraint.cs b/Nu/Nu.Spine/src/PhysicsConstraint.cs index 1ab3000a9a..bac1aca65c 100644 --- a/Nu/Nu.Spine/src/PhysicsConstraint.cs +++ b/Nu/Nu.Spine/src/PhysicsConstraint.cs @@ -53,7 +53,7 @@ public class PhysicsConstraint : IUpdatable { internal bool active; readonly Skeleton skeleton; - float remaining, lastTime; + double remaining, lastTime; public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) { if (data == null) throw new ArgumentNullException("data", "data cannot be null."); @@ -148,7 +148,7 @@ public void Update (Physics physics) { goto case Physics.Update; // Fall through. case Physics.Update: Skeleton skeleton = this.skeleton; - float delta = Math.Max(skeleton.time - lastTime, 0); + double delta = Math.Max(skeleton.time - lastTime, 0); remaining += delta; lastTime = skeleton.time; @@ -158,34 +158,34 @@ public void Update (Physics physics) { ux = bx; uy = by; } else { - float a = remaining, i = inertia, t = data.step, f = skeleton.data.referenceScale, d = -1; - float qx = data.limit * delta, qy = qx * Math.Abs(skeleton.ScaleY); + double a = remaining, i = inertia, t = data.step, f = skeleton.data.referenceScale, d = -1; + double qx = data.limit * delta, qy = qx * Math.Abs(skeleton.ScaleY); qx *= Math.Abs(skeleton.ScaleX); if (x || y) { if (x) { - float u = (ux - bx) * i; - xOffset += u > qx ? qx : u < -qx ? -qx : u; + double u = (ux - bx) * i; + xOffset += (float)(u > qx ? qx : u < -qx ? -qx : u); ux = bx; } if (y) { - float u = (uy - by) * i; - yOffset += u > qy ? qy : u < -qy ? -qy : u; + double u = (uy - by) * i; + yOffset += (float)(u > qy ? qy : u < -qy ? -qy : u); uy = by; } if (a >= t) { - d = (float)Math.Pow(damping, 60 * t); - float m = massInverse * t, e = strength, w = wind * f, g = (Bone.yDown ? -gravity : gravity) * f; + d = Math.Pow(damping, 60 * t); + double m = massInverse * t, e = strength, w = wind * f, g = (Bone.yDown ? -gravity : gravity) * f; do { if (x) { - xVelocity += (w - xOffset * e) * m; - xOffset += xVelocity * t; - xVelocity *= d; + xVelocity += (float)((w - xOffset * e) * m); + xOffset += (float)(xVelocity * t); + xVelocity *= (float)d; } if (y) { - yVelocity -= (g + yOffset * e) * m; - yOffset += yVelocity * t; - yVelocity *= d; + yVelocity -= (float)((g + yOffset * e) * m); + yOffset += (float)(yVelocity * t); + yVelocity *= (float)d; } a -= t; } while (a >= t); @@ -194,8 +194,8 @@ public void Update (Physics physics) { if (y) bone.worldY += yOffset * mix * data.y; } if (rotateOrShearX || scaleX) { - float ca = (float)Math.Atan2(bone.c, bone.a), c, s, mr = 0; - float dx = cx - bone.worldX, dy = cy - bone.worldY; + double ca = Math.Atan2(bone.c, bone.a), c, s, mr = 0; + double dx = cx - bone.worldX, dy = cy - bone.worldY; if (dx > qx) dx = qx; else if (dx < -qx) @@ -206,40 +206,40 @@ public void Update (Physics physics) { dy = -qy; if (rotateOrShearX) { mr = (data.rotate + data.shearX) * mix; - float r = (float)Math.Atan2(dy + ty, dx + tx) - ca - rotateOffset * mr; - rotateOffset += (r - (float)Math.Ceiling(r * MathUtils.InvPI2 - 0.5f) * MathUtils.PI2) * i; + double r = Math.Atan2(dy + ty, dx + tx) - ca - rotateOffset * mr; + rotateOffset += (float)((r - Math.Ceiling(r * MathUtils.InvPI2 - 0.5f) * MathUtils.PI2) * i); r = rotateOffset * mr + ca; - c = (float)Math.Cos(r); - s = (float)Math.Sin(r); + c = Math.Cos(r); + s = Math.Sin(r); if (scaleX) { r = l * bone.WorldScaleX; - if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; + if (r > 0) scaleOffset += (float)((dx * c + dy * s) * i / r); } } else { - c = (float)Math.Cos(ca); - s = (float)Math.Sin(ca); + c = Math.Cos(ca); + s = Math.Sin(ca); float r = l * bone.WorldScaleX; - if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; + if (r > 0) scaleOffset += (float)((dx * c + dy * s) * i / r); } a = remaining; if (a >= t) { - if (d == -1) d = (float)Math.Pow(damping, 60 * t); - float m = massInverse * t, e = strength, w = wind, g = (Bone.yDown ? -gravity : gravity), h = l / f; + if (d == -1) d = Math.Pow(damping, 60 * t); + double m = massInverse * t, e = strength, w = wind, g = (Bone.yDown ? -gravity : gravity), h = l / f; while (true) { a -= t; if (scaleX) { - scaleVelocity += (w * c - g * s - scaleOffset * e) * m; - scaleOffset += scaleVelocity * t; - scaleVelocity *= d; + scaleVelocity += (float)((w * c - g * s - scaleOffset * e) * m); + scaleOffset += (float)(scaleVelocity * t); + scaleVelocity *= (float)d; } if (rotateOrShearX) { - rotateVelocity -= ((w * s + g * c) * h + rotateOffset * e) * m; - rotateOffset += rotateVelocity * t; - rotateVelocity *= d; + rotateVelocity -= (float)(((w * s + g * c) * h + rotateOffset * e) * m); + rotateOffset += (float)(rotateVelocity * t); + rotateVelocity *= (float)d; if (a < t) break; - float r = rotateOffset * mr + ca; - c = (float)Math.Cos(r); - s = (float)Math.Sin(r); + double r = (float)(rotateOffset * mr + ca); + c = Math.Cos(r); + s = Math.Sin(r); } else if (a < t) // break; } @@ -257,33 +257,33 @@ public void Update (Physics physics) { } if (rotateOrShearX) { - float o = rotateOffset * mix, s, c, a; + double o = rotateOffset * mix, s, c, a; if (data.shearX > 0) { - float r = 0; + double r = 0.0; if (data.rotate > 0) { r = o * data.rotate; - s = (float)Math.Sin(r); - c = (float)Math.Cos(r); + s = Math.Sin(r); + c = Math.Cos(r); a = bone.b; - bone.b = c * a - s * bone.d; - bone.d = s * a + c * bone.d; + bone.b = (float)(c * a - s * bone.d); + bone.d = (float)(s * a + c * bone.d); } r += o * data.shearX; - s = (float)Math.Sin(r); - c = (float)Math.Cos(r); + s = Math.Sin(r); + c = Math.Cos(r); a = bone.a; - bone.a = c * a - s * bone.c; - bone.c = s * a + c * bone.c; + bone.a = (float)(c * a - s * bone.c); + bone.c = (float)(s * a + c * bone.c); } else { o *= data.rotate; - s = (float)Math.Sin(o); - c = (float)Math.Cos(o); + s = Math.Sin(o); + c = Math.Cos(o); a = bone.a; - bone.a = c * a - s * bone.c; - bone.c = s * a + c * bone.c; + bone.a = (float)(c * a - s * bone.c); + bone.c = (float)(s * a + c * bone.c); a = bone.b; - bone.b = c * a - s * bone.d; - bone.d = s * a + c * bone.d; + bone.b = (float)(c * a - s * bone.d); + bone.d = (float)(s * a + c * bone.d); } } if (scaleX) { diff --git a/Nu/Nu.Spine/src/Skeleton.cs b/Nu/Nu.Spine/src/Skeleton.cs index 5f9f1e49e5..fe679cfd59 100644 --- a/Nu/Nu.Spine/src/Skeleton.cs +++ b/Nu/Nu.Spine/src/Skeleton.cs @@ -43,7 +43,8 @@ public class Skeleton { internal ExposedList updateCache = new ExposedList(); internal Skin skin; internal float r = 1, g = 1, b = 1, a = 1; - internal float x, y, scaleX = 1, time; + internal float x, y, scaleX = 1; + internal double time; /// Private to enforce usage of ScaleY getter taking Bone.yDown into account. private float scaleY = 1; @@ -103,7 +104,7 @@ public Skin Skin { public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } } /// Returns the skeleton's time. This is used for time-based manipulations, such as . /// - public float Time { get { return time; } set { time = value; } } + public double Time { get { return time; } set { time = value; } } /// Returns the root bone, or null if the skeleton has no bones. public Bone RootBone { diff --git a/Nu/Nu/Assimp/Assimp.fs b/Nu/Nu/Assimp/Assimp.fs index f008116664..88075f0fc6 100644 --- a/Nu/Nu/Assimp/Assimp.fs +++ b/Nu/Nu/Assimp/Assimp.fs @@ -91,7 +91,7 @@ module Assimp = m.A3, m.B3, m.C3, m.D3, m.A4, m.B4, m.C4, m.D4) - let internal ComputePositionKeyFrameIndex (animationTime : single, keys : Assimp.VectorKey array) = + let internal ComputePositionKeyFrameIndex (animationTime : double, keys : Assimp.VectorKey array) = let last = dec keys.Length let mutable low = 0 let mutable high = last @@ -100,16 +100,16 @@ module Assimp = while low <= high && not found do let mid = (low + high) / 2 if mid < last then - let midTime = single keys.[inc mid].Time + let midTime = keys.[inc mid].Time if animationTime < midTime then high <- mid - 1 elif animationTime > midTime then low <- mid + 1 else found <- true; i <- mid else found <- true; i <- last if not found then - i <- if animationTime < single keys.[inc low].Time then low else dec low + i <- if animationTime < keys.[inc low].Time then low else dec low i - let internal ComputeRotationKeyFrameIndex (animationTime : single, keys : Assimp.QuaternionKey array) = + let internal ComputeRotationKeyFrameIndex (animationTime : double, keys : Assimp.QuaternionKey array) = let last = dec keys.Length let mutable low = 0 let mutable high = last @@ -118,16 +118,16 @@ module Assimp = while low <= high && not found do let mid = (low + high) / 2 if mid < last then - let midTime = single keys.[inc mid].Time + let midTime = keys.[inc mid].Time if animationTime < midTime then high <- mid - 1 elif animationTime > midTime then low <- mid + 1 else found <- true; i <- mid else found <- true; i <- last if not found then - i <- if animationTime < single keys.[inc low].Time then low else dec low + i <- if animationTime < keys.[inc low].Time then low else dec low i - let internal ComputeScalingKeyFrameIndex (animationTime : single, keys : Assimp.VectorKey array) = + let internal ComputeScalingKeyFrameIndex (animationTime : double, keys : Assimp.VectorKey array) = let last = dec keys.Length let mutable low = 0 let mutable high = last @@ -136,56 +136,56 @@ module Assimp = while low <= high && not found do let mid = (low + high) / 2 if mid < last then - let midTime = single keys.[inc mid].Time + let midTime = keys.[inc mid].Time if animationTime < midTime then high <- mid - 1 elif animationTime > midTime then low <- mid + 1 else found <- true; i <- mid else found <- true; i <- last if not found then - i <- if animationTime < single keys.[inc low].Time then low else dec low + i <- if animationTime < keys.[inc low].Time then low else dec low i - let internal InterpolatePosition (animationTime : single, positionKeys : Assimp.VectorKey array) = + let internal InterpolatePosition (animationTime : double, positionKeys : Assimp.VectorKey array) = if positionKeys.Length <> 1 then let positionIndex = ComputePositionKeyFrameIndex (animationTime, positionKeys) let positionIndexNext = inc positionIndex % positionKeys.Length let positionKey = positionKeys.[positionIndex] let positionKeyNext = positionKeys.[positionIndexNext] - let deltaTime = single (positionKeyNext.Time - positionKey.Time) - let factor = (animationTime - single positionKey.Time) / deltaTime + let deltaTime = positionKeyNext.Time - positionKey.Time + let factor = (animationTime - positionKey.Time) / deltaTime let start = positionKey.Value let stop = positionKeyNext.Value let delta = stop - start - start + factor * delta + start + single factor * delta else positionKeys.[0].Value - let internal InterpolateRotation (animationTime : single, rotationKeys : Assimp.QuaternionKey array) = + let internal InterpolateRotation (animationTime : double, rotationKeys : Assimp.QuaternionKey array) = if rotationKeys.Length <> 1 then let rotationIndex = ComputeRotationKeyFrameIndex (animationTime, rotationKeys) let rotationIndexNext = inc rotationIndex % rotationKeys.Length let rotationKey = rotationKeys.[rotationIndex] let rotationKeyNext = rotationKeys.[rotationIndexNext] - let deltaTime = single (rotationKeyNext.Time - rotationKey.Time) - let factor = (animationTime - single rotationKey.Time) / deltaTime + let deltaTime = rotationKeyNext.Time - rotationKey.Time + let factor = (animationTime - rotationKey.Time) / deltaTime let startRotation = rotationKey.Value let stopRotation = rotationKeyNext.Value - let result = Assimp.Quaternion.Slerp (startRotation, stopRotation, factor) + let result = Assimp.Quaternion.Slerp (startRotation, stopRotation, single factor) result.Normalize () result else rotationKeys.[0].Value - let internal InterpolateScaling (animationTime : single, scalingKeys : Assimp.VectorKey array) = + let internal InterpolateScaling (animationTime : double, scalingKeys : Assimp.VectorKey array) = if scalingKeys.Length <> 1 then let scalingIndex = ComputeScalingKeyFrameIndex (animationTime, scalingKeys) let scalingIndexNext = inc scalingIndex % scalingKeys.Length let scalingKey = scalingKeys.[scalingIndex] let scalingKeyNext = scalingKeys.[scalingIndexNext] - let deltaTime = single (scalingKeyNext.Time - scalingKey.Time) - let factor = (animationTime - single scalingKey.Time) / deltaTime + let deltaTime = scalingKeyNext.Time - scalingKey.Time + let factor = (animationTime - scalingKey.Time) / deltaTime let start = scalingKey.Value let stop = scalingKeyNext.Value let delta = stop - start - start + factor * delta + start + single factor * delta else scalingKeys.[0].Value [] @@ -568,7 +568,7 @@ module AssimpExtensions = List.tryHead nodes static member private UpdateBoneTransforms - (time : single, + (time : double, boneIds : Dictionary, boneInfos : BoneInfo array, boneWrites : int ref, // OPTIMIZATION: bones writes counter prevents us from traversing nodes in the hierarchy that would be redundant (once per duplicated armature). @@ -584,21 +584,22 @@ module AssimpExtensions = for animation in animations do let animationStartTime = animation.StartTime.Seconds let animationLifeTimeOpt = Option.map (fun (lifeTime : GameTime) -> lifeTime.Seconds) animation.LifeTimeOpt + let animationRate = animation.Rate * Constants.Render.AnimatedModelRateScalar let mutable animationChannel = Unchecked.defaultof<_> if animationChannels.TryGetValue (AnimationChannelKey.make animation.Name node.Name, &animationChannel) then - let localTime = max 0.0f (time - animationStartTime) + let localTime = max 0.0 (time - animationStartTime) if (match animationLifeTimeOpt with Some lifeTime -> localTime < animationStartTime + lifeTime | None -> true) && (match animation.BoneFilterOpt with Some boneFilter -> boneFilter.Contains node.Name | None -> true) then let localTimeScaled = match animation.Playback with | Once -> - localTime * animation.Rate * Constants.Render.AnimatedModelRateScalar + localTime * double animationRate | Loop -> - let length = single animationChannel.RotationKeys.[dec animationChannel.RotationKeys.Length].Time - localTime * animation.Rate * Constants.Render.AnimatedModelRateScalar % length + let length = animationChannel.RotationKeys.[dec animationChannel.RotationKeys.Length].Time + localTime * double animationRate % length | Bounce -> - let length = single animationChannel.RotationKeys.[dec animationChannel.RotationKeys.Length].Time - let localTimeScaled = localTime * animation.Rate * Constants.Render.AnimatedModelRateScalar + let length = animationChannel.RotationKeys.[dec animationChannel.RotationKeys.Length].Time + let localTimeScaled = localTime * double animationRate let remainingTime = localTimeScaled % length if int (localTimeScaled / length) % 2 = 1 then length - remainingTime diff --git a/Nu/Nu/Audio/AudioPlayer.fs b/Nu/Nu/Audio/AudioPlayer.fs index 9da3e27ccd..b877b57feb 100644 --- a/Nu/Nu/Audio/AudioPlayer.fs +++ b/Nu/Nu/Audio/AudioPlayer.fs @@ -244,10 +244,10 @@ type [] SdlAudioPlayer = let loops = match playSongMessage.RepeatLimitOpt with Some repeatLimit -> max 0 (int repeatLimit) | None -> -1 SDL_mixer.Mix_HaltMusic () |> ignore // NOTE: have to stop current song in case it is still fading out, causing the next song not to play. SDL_mixer.Mix_VolumeMusic (int (playSongMessage.Volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume * single SDL_mixer.MIX_MAX_VOLUME)) |> ignore - match SDL_mixer.Mix_FadeInMusicPos (musAsset, loops, int (max Constants.Audio.FadeInSecondsMin playSongMessage.FadeInTime.Seconds * 1000.0f), double playSongMessage.StartTime.Seconds) with + match SDL_mixer.Mix_FadeInMusicPos (musAsset, loops, int (max Constants.Audio.FadeInSecondsMin playSongMessage.FadeInTime.Seconds * 1000.0), double playSongMessage.StartTime.Seconds) with | -1 -> // HACK: start time exceeded length of track, so starting over. - SDL_mixer.Mix_FadeInMusicPos (musAsset, loops, int (max Constants.Audio.FadeInSecondsMin playSongMessage.FadeInTime.Seconds * 1000.0f), 0.0) |> ignore + SDL_mixer.Mix_FadeInMusicPos (musAsset, loops, int (max Constants.Audio.FadeInSecondsMin playSongMessage.FadeInTime.Seconds * 1000.0), 0.0) |> ignore | _ -> () audioPlayer.SongOpt <- Some (playSongMessage, musAsset) | None -> @@ -288,7 +288,7 @@ type [] SdlAudioPlayer = if SDL_mixer.Mix_PlayingMusic () = 1 then if fadeOutTime <> GameTime.zero && SDL_mixer.Mix_FadingMusic () <> SDL_mixer.Mix_Fading.MIX_FADING_OUT then - SDL_mixer.Mix_FadeOutMusic (int (fadeOutTime.Seconds * 1000.0f)) |> ignore + SDL_mixer.Mix_FadeOutMusic (int (fadeOutTime.Seconds * 1000.0)) |> ignore else SDL_mixer.Mix_HaltMusic () |> ignore diff --git a/Nu/Nu/Core/Behavior.fs b/Nu/Nu/Core/Behavior.fs index 9c5686e647..b7d9716a11 100644 --- a/Nu/Nu/Core/Behavior.fs +++ b/Nu/Nu/Core/Behavior.fs @@ -167,7 +167,7 @@ module Behavior = map (fun a -> let local = a % stride if bounce then - if int (a / stride) % 2 = 0 + if int64 (a / stride) % 2L = 0L then local else stride - local else local) diff --git a/Nu/Nu/Core/Constants.fs b/Nu/Nu/Core/Constants.fs index cb056ecd3f..b5fde49dcc 100644 --- a/Nu/Nu/Core/Constants.fs +++ b/Nu/Nu/Core/Constants.fs @@ -330,11 +330,11 @@ module Audio = let [] MasterSongVolumeDefault = 1.0f let [] SoundVolumeDefault = 1.0f let [] SongVolumeDefault = 1.0f - let [] FadeOutTimeDefault = GameTime.ofSeconds 0.5f - let [] SongResumptionMax = GameTime.ofSeconds 90.0f // HACK: prevents songs from starting over too often due to hack in SdlAudioPlayer.playSong. + let [] FadeOutTimeDefault = GameTime.ofSeconds 0.5 + let [] SongResumptionMax = GameTime.ofSeconds 90.0 // HACK: prevents songs from starting over too often due to hack in SdlAudioPlayer.playSong. let [] Frequency = 44100 let [] BufferSize = 1024 - let [] FadeInSecondsMin = 0.1f // NOTE: Mix_FadeInMusicPos seems to sometimes cause audio 'popping' when starting a song, so a minimum fade is used instead. + let [] FadeInSecondsMin = 0.1 // NOTE: Mix_FadeInMusicPos seems to sometimes cause audio 'popping' when starting a song, so a minimum fade is used instead. [] module Physics = diff --git a/Nu/Nu/Core/GameTime.fs b/Nu/Nu/Core/GameTime.fs index 98429b0b0b..995f937ca8 100644 --- a/Nu/Nu/Core/GameTime.fs +++ b/Nu/Nu/Core/GameTime.fs @@ -44,7 +44,7 @@ type GameTimeConverter () = let gameTime = source :?> GameTime match gameTime with | UpdateTime time -> Number (string time, ValueNone) :> obj - | TickTime time -> Number (string (single time / single Stopwatch.Frequency), ValueNone) :> obj + | TickTime time -> Number (string (double time / double Stopwatch.Frequency), ValueNone) :> obj elif destType = typeof then source else failconv "Invalid GameTimeConverter conversion to source." None @@ -59,7 +59,7 @@ type GameTimeConverter () = | Number (time, _) -> match Constants.GameTime.DesiredFrameRate with | StaticFrameRate _ -> UpdateTime (Int64.Parse time) :> obj - | DynamicFrameRate _ -> TickTime (int64 (Single.Parse time * single Stopwatch.Frequency)) :> obj + | DynamicFrameRate _ -> TickTime (int64 (Double.Parse time * double Stopwatch.Frequency)) :> obj | _ -> failconv "Invalid GameTimeConverter conversion from source." (Some symbol) | :? GameTime -> source | _ -> failconv "Invalid GameTimeConverter conversion from source." None @@ -98,40 +98,40 @@ and [ failwith "Cannot apply operation to mixed GameTimes." /// Construct a game time from updates or clock time. - static member make updateTime (clockTime : single) = + static member make updateTime (clockTime : double) = match Constants.GameTime.DesiredFrameRate with | StaticFrameRate _ -> UpdateTime updateTime - | DynamicFrameRate _ -> TickTime (int64 (clockTime * single Stopwatch.Frequency)) + | DynamicFrameRate _ -> TickTime (int64 (clockTime * double Stopwatch.Frequency)) /// Construct a game time from a number of updates assuming desired frame rate is met. static member ofUpdates updates = match Constants.GameTime.DesiredFrameRate with | StaticFrameRate _ -> UpdateTime updates - | DynamicFrameRate frameRate -> TickTime (int64 (1.0f / single frameRate * single updates * single Stopwatch.Frequency)) + | DynamicFrameRate frameRate -> TickTime (int64 (1.0 / double frameRate * double updates * double Stopwatch.Frequency)) /// Construct a game time from an amount of seconds assuming desired frame rate was met. static member ofSeconds seconds = match Constants.GameTime.DesiredFrameRate with - | StaticFrameRate frameRate -> UpdateTime (int64 (single frameRate * seconds)) - | DynamicFrameRate _ -> TickTime (int64 (seconds * single Stopwatch.Frequency)) + | StaticFrameRate frameRate -> UpdateTime (int64 (double frameRate * seconds)) + | DynamicFrameRate _ -> TickTime (int64 (seconds * double Stopwatch.Frequency)) /// Get the number of updates assuming desired frame rate is met. static member toUpdates time = match struct (Constants.GameTime.DesiredFrameRate, time) with | struct (_, UpdateTime time) -> time - | struct (DynamicFrameRate frameRate, TickTime time) -> int64 (single time / (1.0f / single frameRate)) + | struct (DynamicFrameRate frameRate, TickTime time) -> int64 (double time / (1.0 / double frameRate)) | struct (_, _) -> failwith "Cannot apply operation to mixed GameTimes." /// Get the total amount of seconds assuming desired frame rate is met. static member toSeconds time = match struct (Constants.GameTime.DesiredFrameRate, time) with - | struct (StaticFrameRate frameRate, UpdateTime time) -> 1.0f / single frameRate * single time - | struct (_, TickTime time) -> single time / single Stopwatch.Frequency + | struct (StaticFrameRate frameRate, UpdateTime time) -> 1.0 / double frameRate * double time + | struct (_, TickTime time) -> double time / double Stopwatch.Frequency | struct (_, _) -> failwith "Cannot apply operation to mixed GameTimes." /// Get the total amount of milliseconds assuming desired frame rate is met. static member toMilliseconds time = - GameTime.toSeconds time * 1000.0f + GameTime.toSeconds time * 1000.0 /// Equate GameTimes. static member equals left right = @@ -147,8 +147,8 @@ and [ (single (currentTime - startTime)) / single lifeTime |> max 0.0f |> min 1.0f - | struct (TickTime startTime, TickTime currentTime, TickTime lifeTime) -> single (currentTime - startTime) / single lifeTime |> max 0.0f |> min 1.0f + | struct (UpdateTime startTime, UpdateTime currentTime, UpdateTime lifeTime) -> (double (currentTime - startTime)) / double lifeTime |> max 0.0 |> min 1.0 + | struct (TickTime startTime, TickTime currentTime, TickTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> max 0.0 |> min 1.0 | struct (_, _, _) -> failwith "Cannot apply operation to mixed GameTimes." static member (+) (left, right) = GameTime.ap (+) (+) left right @@ -157,20 +157,18 @@ and [ left / right * Stopwatch.Frequency |> int64) left right static member (%) (left, right) = GameTime.ap (%) (fun left right -> left % right) left right static member op_Implicit (i : int64) = UpdateTime i - static member op_Implicit (s : single) = TickTime (int64 (s * single Stopwatch.Frequency)) - static member op_Explicit time = match time with UpdateTime time -> int time | TickTime time -> int (single time / single Stopwatch.Frequency) - static member op_Explicit time = match time with UpdateTime time -> int64 time | TickTime time -> int64 (single time / single Stopwatch.Frequency) - static member op_Explicit time = match time with UpdateTime time -> single time | TickTime time -> single (single time / single Stopwatch.Frequency) - static member op_Explicit time = match time with UpdateTime time -> double time | TickTime time -> double (single time / single Stopwatch.Frequency) + static member op_Implicit (d : double) = TickTime (int64 (d * double Stopwatch.Frequency)) + static member op_Explicit time = match time with UpdateTime time -> int64 time | TickTime time -> int64 (double time / double Stopwatch.Frequency) + static member op_Explicit time = match time with UpdateTime time -> double time | TickTime time -> double (double time / double Stopwatch.Frequency) static member get_Zero () = GameTime.zero static member isZero time = GameTime.unary isZero isZero time static member notZero time = GameTime.unary notZero notZero time - static member zero = GameTime.ofSeconds 0.0f + static member zero = GameTime.ofSeconds 0.0 static member epsilon = match Constants.GameTime.DesiredFrameRate with StaticFrameRate _ -> UpdateTime 1L | DynamicFrameRate _ -> TickTime 1L static member min (left : GameTime) right = if left <= right then left else right static member max (left : GameTime) right = if left >= right then left else right - static member MinValue = GameTime.make Int64.MinValue Single.MinValue - static member MaxValue = GameTime.make Int64.MaxValue Single.MaxValue + static member MinValue = GameTime.make Int64.MinValue Double.MinValue + static member MaxValue = GameTime.make Int64.MaxValue Double.MaxValue /// The total amount of elapsed updates. member this.Updates = @@ -180,10 +178,20 @@ and [ totalTime + keyFrame.KeyFrameLength) GameTime.zero keyFrames if totalTime <> GameTime.zero then let moduloTime = localTime % totalTime - let bouncing = int (localTime / totalTime) % 2 = 1 + let bouncing = int64 (localTime / totalTime) % 2L = 1L let bounceTime = if bouncing then totalTime - moduloTime else moduloTime selectKeyFrames2 bounceTime Once keyFrames else (GameTime.zero, Array.head keyFrames, Array.head keyFrames) @@ -125,20 +125,20 @@ module EffectSystem = let private evalInset (celSize : Vector2i) celCount celRun delay playback effectSystem = // TODO: stop assuming that animation sheets are fully and evenly populated when flipping! - let celUnmodulated = int (effectSystem.EffectTime / delay) + let celUnmodulated = int64 (effectSystem.EffectTime / delay) let cel = celUnmodulated % celCount let celI = cel % celRun let celJ = cel / celRun let bouncing = match playback with - | Bounce -> celUnmodulated % (celCount * 2) >= celCount + | Bounce -> celUnmodulated % (celCount * 2L) >= celCount | Once | Loop -> false let (celI, celJ) = if bouncing then (celRun - celI, (celRun % celCount) - celJ) else (celI, celJ) - let celX = celI * celSize.X - let celY = celJ * celSize.Y + let celX = celI * int64 celSize.X + let celY = celJ * int64 celSize.Y let celPosition = Vector2 (single celX, single celY) let celSize = Vector2 (single celSize.X, single celSize.Y) Box2 (celPosition, celSize) @@ -177,7 +177,7 @@ module EffectSystem = evalContent content slice history effectSystem and private evalProgress keyFrameTime keyFrameLength effectSystem = - let progress = if GameTime.isZero keyFrameLength then 1.0f else single keyFrameTime / single keyFrameLength + let progress = if GameTime.isZero keyFrameLength then 1.0f else single (double keyFrameTime / double keyFrameLength) let progress = progress + effectSystem.EffectProgressOffset if progress > 1.0f then progress - 1.0f else progress @@ -406,7 +406,7 @@ module EffectSystem = if GameTime.notZero delay && celRun <> 0 then // compute cel - let cel = int (effectSystem.EffectTime / delay) + let cel = int64 (effectSystem.EffectTime / delay) // eval inset let inset = evalInset celSize celCount celRun delay playback effectSystem @@ -653,8 +653,8 @@ module EffectSystem = let effectTime = effectSystem.EffectTimeOriginal - slice.SliceTime let slice = { slice with Elevation = slice.Elevation + shift } let slice = evalAspects emitterAspects slice { effectSystem with EffectTime = effectSystem.EffectTime - effectTime } - let emitCountLastFrame = single (effectSystem.EffectTime - effectTime - slice.SliceDelta) * rate - let emitCountThisFrame = single (effectSystem.EffectTime - effectTime) * rate + let emitCountLastFrame = double (effectSystem.EffectTime - effectTime - slice.SliceDelta) * double rate + let emitCountThisFrame = double (effectSystem.EffectTime - effectTime) * double rate let emitCount = int emitCountThisFrame - int emitCountLastFrame let effectSystem = Array.fold (fun effectSystem _ -> diff --git a/Nu/Nu/Particles/Particles.fs b/Nu/Nu/Particles/Particles.fs index 2d34097341..7bcb8e64f3 100644 --- a/Nu/Nu/Particles/Particles.fs +++ b/Nu/Nu/Particles/Particles.fs @@ -17,8 +17,9 @@ type [] Life = /// The progress made through the instance's life. static member getProgress (time : GameTime) life = - if life.LifeTimeOpt.NotZero - then single (time - life.StartTime) * life.ProgressScalar + if life.LifeTimeOpt.NotZero then + let deltaTime = time - life.StartTime + single (double deltaTime * double life.ProgressScalar) else 0.0f /// The progress made through the instance's life within a sub-range. @@ -38,7 +39,7 @@ type [] Life = static member make startTime lifeTimeOpt = { StartTime = startTime LifeTimeOpt = lifeTimeOpt - ProgressScalar = 1.0f / single lifeTimeOpt } + ProgressScalar = single (1.0 / double lifeTimeOpt) } /// A spatial constraint. type Constraint = @@ -198,7 +199,7 @@ module Transformer = /// Accelerate bodies both linearly and angularly. let accelerate (delta : GameTime) (bodies : Body SArray) = let mutable i = 0 - let scalar = single delta + let scalar = delta |> double |> single while i < bodies.Length do let body = &bodies.[i] body.Position <- body.Position + body.LinearVelocity * scalar @@ -207,7 +208,7 @@ module Transformer = /// Constrain bodies. let rec constrain (delta : GameTime) c (bodies : Body SArray) = - let scalar = single delta + let scalar = delta |> double |> single match c with | Sphere (radius, center) -> let mutable i = 0 @@ -246,7 +247,7 @@ module Transformer = fun delta _ c bodies -> match force with | Gravity gravity -> - let scalar = single delta + let scalar = delta |> double |> single let mutable i = 0 while i < bodies.Length do let body = &bodies.[i] @@ -254,7 +255,7 @@ module Transformer = i <- inc i Output.empty | Attractor (position, radius, force) -> - let scalar = single delta + let scalar = delta |> double |> single let mutable i = 0 while i < bodies.Length do let body = &bodies.[i] @@ -267,7 +268,7 @@ module Transformer = i <- inc i Output.empty | Drag (linearDrag, angularDrag) -> - let scalar = single delta + let scalar = delta |> double |> single let mutable i = 0 while i < bodies.Length do let body = &bodies.[i] @@ -781,8 +782,8 @@ type [] StaticSpriteEmitter<'a when 'a :> Particle and 'a : e // emit new particles if alive if Life.getAlive time emitter.Life then - let emitCount = single localTime * emitter.ParticleRate - let emitCountPrevious = single localTimePrevious * emitter.ParticleRate + let emitCount = double localTime * double emitter.ParticleRate + let emitCountPrevious = double localTimePrevious * double emitter.ParticleRate let emitCount = int emitCount - int emitCountPrevious for _ in 0 .. emitCount - 1 do StaticSpriteEmitter<'a>.emit time emitter @@ -908,7 +909,7 @@ module BasicStaticSpriteEmitter = let makeDefault time lifeTimeOpt particleLifeTimeMaxOpt particleRate particleMax = let image = asset Assets.Default.PackageName Assets.Default.ImageName let particleSeed = - { Life = Life.make GameTime.zero (GameTime.ofSeconds 2.0f) + { Life = Life.make GameTime.zero (GameTime.ofSeconds 2.0) Body = Body.defaultBody Offset = v3Zero Size = Constants.Engine.Particle2dSizeDefault @@ -1034,8 +1035,8 @@ type [] StaticBillboardEmitter<'a when 'a :> Particle and 'a // emit new particles if alive if Life.getAlive time emitter.Life then - let emitCount = single localTime * emitter.ParticleRate - let emitCountPrevious = single localTimePrevious * emitter.ParticleRate + let emitCount = double localTime * double emitter.ParticleRate + let emitCountPrevious = double localTimePrevious * double emitter.ParticleRate let emitCount = int emitCount - int emitCountPrevious for _ in 0 .. emitCount - 1 do StaticBillboardEmitter<'a>.emit time emitter @@ -1164,7 +1165,7 @@ module BasicStaticBillboardEmitter = /// Make the default basic billboard particle emitter. let makeDefault time lifeTimeOpt particleLifeTimeMaxOpt particleRate particleMax = let particleSeed = - { Life = Life.make GameTime.zero (GameTime.ofSeconds 2.0f) + { Life = Life.make GameTime.zero (GameTime.ofSeconds 2.0) Body = Body.defaultBody Offset = v3Zero Size = Constants.Engine.Particle3dSizeDefault diff --git a/Nu/Nu/Physics/AetherPhysicsEngine.fs b/Nu/Nu/Physics/AetherPhysicsEngine.fs index 2d4ab9b6a0..a4169c6584 100644 --- a/Nu/Nu/Physics/AetherPhysicsEngine.fs +++ b/Nu/Nu/Physics/AetherPhysicsEngine.fs @@ -1245,7 +1245,7 @@ and [] AetherPhysicsEngine = // manually sleep static bodies since aether won't sleep them itself if body.BodyType = Dynamics.BodyType.Static then body.Awake <- false - static member private applyGravity physicsStepAmount physicsEngine = + static member private stepGravity (physicsStepAmount : single) physicsEngine = for bodyEntry in physicsEngine.Bodies do let (gravity, body) = bodyEntry.Value if body.BodyType = Dynamics.BodyType.Dynamic then @@ -1388,10 +1388,10 @@ and [] AetherPhysicsEngine = member physicsEngine.HandleMessage physicsMessage = AetherPhysicsEngine.handlePhysicsMessage physicsEngine physicsMessage - member physicsEngine.TryIntegrate stepTime = + member physicsEngine.TryIntegrate gameDelta = // constrain step time - let stepTime = stepTime.Seconds + let stepTime = gameDelta.SecondsF let stepTime = if stepTime > 0.0f && stepTime < 0.001f then 0.001f elif stepTime > 0.1f then 0.1f @@ -1399,7 +1399,7 @@ and [] AetherPhysicsEngine = // integrate only when time has passed if stepTime > 0.0f then - AetherPhysicsEngine.applyGravity stepTime physicsEngine + AetherPhysicsEngine.stepGravity stepTime physicsEngine physicsEngine.PhysicsContext.Step stepTime AetherPhysicsEngine.createIntegrationMessagesAndSleepAwakeStaticBodies physicsEngine let gravity = (physicsEngine :> PhysicsEngine).Gravity.V2 diff --git a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs index 5b545d5500..d3b88effd2 100644 --- a/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs +++ b/Nu/Nu/Physics/Box2dNetPhysicsEngine.fs @@ -1662,10 +1662,10 @@ type [] Box2dNetPhysicsEngine = member physicsEngine.HandleMessage physicsMessage = Box2dNetPhysicsEngine.handlePhysicsMessage physicsEngine physicsMessage - member physicsEngine.TryIntegrate stepTime = + member physicsEngine.TryIntegrate gameDelta = // constrain step time - let stepTime = stepTime.Seconds + let stepTime = gameDelta.SecondsF let stepTime = if stepTime > 0.0f && stepTime < 0.001f then 0.001f elif stepTime > 0.1f then 0.1f diff --git a/Nu/Nu/Physics/JoltPhysicsEngine.fs b/Nu/Nu/Physics/JoltPhysicsEngine.fs index 25907df609..8d7836b98b 100644 --- a/Nu/Nu/Physics/JoltPhysicsEngine.fs +++ b/Nu/Nu/Physics/JoltPhysicsEngine.fs @@ -1480,13 +1480,14 @@ and [] JoltPhysicsEngine = member physicsEngine.HandleMessage physicsMessage = JoltPhysicsEngine.handlePhysicsMessage physicsEngine physicsMessage - member physicsEngine.TryIntegrate stepTime = + member physicsEngine.TryIntegrate gameDelta = // integrate only when time has passed - if not stepTime.IsZero then + let stepTime = gameDelta.SecondsF + if stepTime > 0.0f then // update non-character physics, logging on error (integration should still advance sim regardless of error) - match physicsEngine.PhysicsContext.Update (stepTime.Seconds, Constants.Physics.Collision3dSteps, physicsEngine.JobSystem) with + match physicsEngine.PhysicsContext.Update (stepTime, Constants.Physics.Collision3dSteps, physicsEngine.JobSystem) with | PhysicsUpdateError.ManifoldCacheFull as error -> Log.warnOnce ("Jolt Physics internal error: " + scstring error + ". Consider increasing Constants.Physics." + @@ -1529,8 +1530,8 @@ and [] JoltPhysicsEngine = else character.LinearVelocity.MapY (max 0.0f) else let characterGravity = physicsEngine.PhysicsContext.Gravity * characterGravityFactor - character.LinearVelocity + characterGravity * stepTime.Seconds - character.ExtendedUpdate (stepTime.Seconds, characterUpdateSettings, &characterLayer, physicsEngine.PhysicsContext) + character.LinearVelocity + characterGravity * stepTime + character.ExtendedUpdate (stepTime, characterUpdateSettings, &characterLayer, physicsEngine.PhysicsContext) // update constraints for bodyConstraintEntry in physicsEngine.BodyConstraintBreakingPoints do diff --git a/Nu/Nu/Physics/PhysicsEngine.fs b/Nu/Nu/Physics/PhysicsEngine.fs index b80fcc693f..0721a1e8cd 100644 --- a/Nu/Nu/Physics/PhysicsEngine.fs +++ b/Nu/Nu/Physics/PhysicsEngine.fs @@ -310,7 +310,7 @@ type PhysicsEngine = abstract HandleMessage : message : PhysicsMessage -> unit /// Attempt to integrate the physics system one step. - abstract TryIntegrate : delta : GameTime -> IntegrationMessage SArray option + abstract TryIntegrate : gameDelta : GameTime -> IntegrationMessage SArray option /// Attempt to render physics with the given physics-engine-specific render context. abstract TryRender : renderContext : PhysicsEngineRenderContext -> unit diff --git a/Nu/Nu/World/WorldConstants.fs b/Nu/Nu/World/WorldConstants.fs index 44e9228132..ee3976c40f 100644 --- a/Nu/Nu/World/WorldConstants.fs +++ b/Nu/Nu/World/WorldConstants.fs @@ -11,8 +11,8 @@ module Dissolve = /// The default 'dissolving' transition behavior of screens. let Default = - { IncomingTime = GameTime.ofSeconds 0.5f - OutgoingTime = GameTime.ofSeconds 1.0f + { IncomingTime = GameTime.ofSeconds 0.5 + OutgoingTime = GameTime.ofSeconds 1.0 DissolveImage = Assets.Default.Black } [] @@ -21,5 +21,5 @@ module Slide = /// The default 'slide show' behavior of slide screens. let Default = { DissolveDescriptor = Dissolve.Default - IdlingTime = GameTime.ofSeconds 1.0f + IdlingTime = GameTime.ofSeconds 1.0 SlideImageOpt = Some Assets.Default.NuSlide } \ No newline at end of file diff --git a/Nu/Nu/World/WorldDispatchers.fs b/Nu/Nu/World/WorldDispatchers.fs index 6e44381beb..503a96e924 100644 --- a/Nu/Nu/World/WorldDispatchers.fs +++ b/Nu/Nu/World/WorldDispatchers.fs @@ -322,7 +322,7 @@ type Character2dDispatcher () = [define Entity.MountOpt None define Entity.CelSize (v2 28.0f 28.0f) define Entity.CelRun 8 - define Entity.AnimationDelay (GameTime.ofSeconds (1.0f / 15.0f)) + define Entity.AnimationDelay (GameTime.ofSeconds (1.0 / 15.0)) define Entity.BodyType Dynamic define Entity.AngularFactor v3Zero define Entity.SleepingAllowed true diff --git a/Nu/Nu/World/WorldFacets.fs b/Nu/Nu/World/WorldFacets.fs index 1dff1cbaa0..897b03b242 100644 --- a/Nu/Nu/World/WorldFacets.fs +++ b/Nu/Nu/World/WorldFacets.fs @@ -116,10 +116,12 @@ type AnimatedSpriteFacet () = let celRun = entity.GetCelRun world if celCount <> 0 && celRun <> 0 then let localTime = world.GameTime - startTime - let cel = int (localTime / entity.GetAnimationDelay world) % celCount * entity.GetAnimationStride world + let animationDelay = entity.GetAnimationDelay world + let animationStride = entity.GetAnimationStride world + let cel = int64 (localTime / animationDelay) % int64 celCount * int64 animationStride let celSize = entity.GetCelSize world - let celI = cel % celRun - let celJ = cel / celRun + let celI = cel % int64 celRun + let celJ = cel / int64 celRun let celX = single celI * celSize.X let celY = single celJ * celSize.Y let inset = box2 (v2 celX celY) celSize @@ -131,7 +133,7 @@ type AnimatedSpriteFacet () = define Entity.CelSize (Vector2 (32.0f, 32.0f)) define Entity.CelCount 16 define Entity.CelRun 4 - define Entity.AnimationDelay (GameTime.ofSeconds (1.0f / 15.0f)) + define Entity.AnimationDelay (GameTime.ofSeconds (1.0 / 15.0)) define Entity.AnimationStride 1 define Entity.AnimationSheet Assets.Default.AnimatedSprite define Entity.ClipOpt None @@ -351,10 +353,10 @@ type BasicStaticSpriteEmitterFacet () = define Entity.EmitterClipOpt None define Entity.EmitterImage Assets.Default.Image define Entity.EmitterLifeTimeOpt GameTime.zero - define Entity.ParticleLifeTimeMaxOpt (GameTime.ofSeconds 1.0f) + define Entity.ParticleLifeTimeMaxOpt (GameTime.ofSeconds 1.0) define Entity.ParticleRate (match Constants.GameTime.DesiredFrameRate with StaticFrameRate _ -> 1.0f | DynamicFrameRate _ -> 60.0f) define Entity.ParticleMax 60 - define Entity.BasicParticleSeed { Life = Particles.Life.make GameTime.zero (GameTime.ofSeconds 1.0f); Body = Particles.Body.defaultBody; Size = Constants.Engine.Particle2dSizeDefault; Offset = v3Zero; Inset = box2Zero; Color = Color.One; Emission = Color.Zero; Flip = FlipNone } + define Entity.BasicParticleSeed { Life = Particles.Life.make GameTime.zero (GameTime.ofSeconds 1.0); Body = Particles.Body.defaultBody; Size = Constants.Engine.Particle2dSizeDefault; Offset = v3Zero; Inset = box2Zero; Color = Color.One; Emission = Color.Zero; Flip = FlipNone } define Entity.EmitterConstraint Particles.Constraint.empty define Entity.EmitterStyle "BasicStaticSpriteEmitter" nonPersistent Entity.ParticleSystem Particles.ParticleSystem.empty] @@ -2153,8 +2155,8 @@ type SpineSkeletonFacet () = spineSkeletonState.SpineSkeleton.ScaleX <- scaleX spineSkeletonState.SpineSkeleton.ScaleY <- scaleY spineSkeletonState.SpineAnimationState.TimeScale <- entity.GetSpineAnimationSpeed world - spineSkeletonState.SpineSkeleton.Update gameDelta.Seconds - spineSkeletonState.SpineAnimationState.Update gameDelta.Seconds + spineSkeletonState.SpineSkeleton.Update gameDelta.SecondsF + spineSkeletonState.SpineAnimationState.Update gameDelta.SecondsF spineSkeletonState.SpineAnimationState.Apply spineSkeletonState.SpineSkeleton |> ignore spineSkeletonState.SpineSkeleton.UpdateWorldTransform Spine.Skeleton.Physics.Update spineSkeletonState.SpineAnimationState.remove_Start startDelegate @@ -2818,10 +2820,12 @@ type AnimatedBillboardFacet () = let celRun = entity.GetCelRun world if celCount <> 0 && celRun <> 0 then let localTime = world.GameTime - startTime - let cel = int (localTime / entity.GetAnimationDelay world) % celCount * entity.GetAnimationStride world + let animationDelay = entity.GetAnimationDelay world + let animationStride = entity.GetAnimationStride world + let cel = int64 (localTime / animationDelay) % int64 celCount * int64 animationStride let celSize = entity.GetCelSize world - let celI = cel % celRun - let celJ = cel / celRun + let celI = cel % int64 celRun + let celJ = cel / int64 celRun let celX = single celI * celSize.X let celY = single celJ * celSize.Y let inset = box2 (v2 celX celY) celSize @@ -2833,7 +2837,7 @@ type AnimatedBillboardFacet () = define Entity.CelSize (Vector2 (32.0f, 32.0f)) define Entity.CelCount 16 define Entity.CelRun 4 - define Entity.AnimationDelay (GameTime.ofSeconds (1.0f / 15.0f)) + define Entity.AnimationDelay (GameTime.ofSeconds (1.0 / 15.0)) define Entity.AnimationStride 1 define Entity.MaterialProperties MaterialProperties.defaultProperties define Entity.Material Material.defaultMaterial @@ -3049,10 +3053,10 @@ type BasicStaticBillboardEmitterFacet () = define Entity.EmitterMaterialProperties MaterialProperties.defaultProperties define Entity.EmitterMaterial Material.defaultMaterial define Entity.EmitterLifeTimeOpt GameTime.zero - define Entity.ParticleLifeTimeMaxOpt (GameTime.ofSeconds 1.0f) + define Entity.ParticleLifeTimeMaxOpt (GameTime.ofSeconds 1.0) define Entity.ParticleRate (match Constants.GameTime.DesiredFrameRate with StaticFrameRate _ -> 1.0f | DynamicFrameRate _ -> 60.0f) define Entity.ParticleMax 60 - define Entity.BasicParticleSeed { Life = Particles.Life.make GameTime.zero (GameTime.ofSeconds 1.0f); Body = Particles.Body.defaultBody; Size = v3Dup 0.25f; Offset = v3Zero; Inset = box2Zero; Color = Color.One; Emission = Color.Zero; Flip = FlipNone } + define Entity.BasicParticleSeed { Life = Particles.Life.make GameTime.zero (GameTime.ofSeconds 1.0); Body = Particles.Body.defaultBody; Size = v3Dup 0.25f; Offset = v3Zero; Inset = box2Zero; Color = Color.One; Emission = Color.Zero; Flip = FlipNone } define Entity.EmitterConstraint Particles.Constraint.empty define Entity.EmitterStyle "BasicStaticBillboardEmitter" define Entity.EmitterRenderStyle Deferred @@ -3839,7 +3843,7 @@ module TraversalInterpolatedFacetExtensions = match prevOpt with | ValueSome (previousTime, previousValue) -> let deltaTime = time - previousTime - let deltaTime = deltaTime.Seconds + let deltaTime = deltaTime.SecondsF if deltaTime > 0.0f then (sum + 0.5f * (previousValue + value) * deltaTime, totalTime + deltaTime, ValueSome (time, value)) else (sum, totalTime, ValueSome (time, value)) @@ -3871,7 +3875,7 @@ module TraversalInterpolatedFacetExtensions = match prevOpt with | ValueSome (previousTime, previousRotation) -> let deltaTime = time - previousTime - let deltaTime = deltaTime.Seconds + let deltaTime = deltaTime.SecondsF if deltaTime > 0.0f then let midpoint = Quaternion.Slerp (previousRotation, rotation, 0.5f) (sum + midpoint * deltaTime, totalTime + deltaTime, ValueSome (time, rotation)) diff --git a/Nu/Nu/World/WorldScreen.fs b/Nu/Nu/World/WorldScreen.fs index ca468a9cb0..243d8889b2 100644 --- a/Nu/Nu/World/WorldScreen.fs +++ b/Nu/Nu/World/WorldScreen.fs @@ -610,7 +610,7 @@ module WorldScreenModule = /// Compute angular velocity for the given turn speed and navDirection. static member nav3dFace turnSpeed (rotation : Quaternion) (navDirection : Vector3) (world : World) = - let deltaTime = let gameDelta = world.GameDelta in gameDelta.Seconds + let deltaTime = let gameDelta = world.GameDelta in gameDelta.SecondsF let navRotationDesired = Quaternion.CreateFromAxisAngle (v3Up, atan2 navDirection.X navDirection.Z + MathF.PI) let navSign = (rotation.Forward.Cross navRotationDesired.Forward).Y let navAngleBetweenOpt = rotation.Forward.AngleBetween navRotationDesired.Forward @@ -627,7 +627,7 @@ module WorldScreenModule = /// Compute navigation information that results in following the given destination. static member nav3dFollow distanceMinOpt distanceMaxOpt moveSpeed turnSpeed (position : Vector3) (rotation : Quaternion) (destination : Vector3) screen (world : World) = - let deltaTime = let gameDelta = world.GameDelta in gameDelta.Seconds + let deltaTime = let gameDelta = world.GameDelta in gameDelta.SecondsF let distance = (destination - position).Magnitude if (Option.isNone distanceMinOpt || distance > distanceMinOpt.Value) && (Option.isNone distanceMaxOpt || distance <= distanceMaxOpt.Value) then diff --git a/Projects/Sand Box 2d/FluidSim.fs b/Projects/Sand Box 2d/FluidSim.fs index 29fdbb11a3..16cb440bfe 100644 --- a/Projects/Sand Box 2d/FluidSim.fs +++ b/Projects/Sand Box 2d/FluidSim.fs @@ -260,7 +260,7 @@ type FluidSimDispatcher () = paddle.SetBodyType Kinematic world paddle.SetLinearVelocity (v3 50f 0f 0f) world coroutine world.Launcher { - do! Coroutine.sleep (GameTime.ofSeconds 10f) + do! Coroutine.sleep (GameTime.ofSeconds 10) World.destroyEntity paddle world } // switch screen button diff --git a/Projects/Terra Firma/Character.fs b/Projects/Terra Firma/Character.fs index eee6fc7054..5c6c8e1902 100644 --- a/Projects/Terra Firma/Character.fs +++ b/Projects/Terra Firma/Character.fs @@ -234,7 +234,7 @@ type CharacterDispatcher () = let turnVelocity = (if World.isKeyboardKeyDown KeyboardKey.Right world then -turnSpeed else 0.0f) + (if World.isKeyboardKeyDown KeyboardKey.Left world then turnSpeed else 0.0f) - let rotation = if turnVelocity <> 0.0f then rotation * Quaternion.CreateFromAxisAngle (v3Up, turnVelocity * world.GameDelta.Seconds) else rotation + let rotation = if turnVelocity <> 0.0f then rotation * Quaternion.CreateFromAxisAngle (v3Up, turnVelocity * world.GameDelta.SecondsF) else rotation // apply changes entity.SetLinearVelocity (walkVelocity.WithY 0.0f + entity.GetLinearVelocity world * v3Up) world From bf578d933910504d9cd94654e29011b32cfbd72b Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 12 Nov 2025 00:15:05 -0500 Subject: [PATCH 28/52] Made GameTime.progress return single. --- Nu/Nu/Core/GameTime.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nu/Nu/Core/GameTime.fs b/Nu/Nu/Core/GameTime.fs index 995f937ca8..5545e9a399 100644 --- a/Nu/Nu/Core/GameTime.fs +++ b/Nu/Nu/Core/GameTime.fs @@ -144,11 +144,11 @@ and [ if leftTime < rightTime then -1 elif leftTime > rightTime then 1 else 0 | struct (_, _) -> failwith "Cannot apply operation to mixed GameTimes." - /// The progress of time down a bounded time range. + /// The progress of time down a unit-bounded range. static member progress startTime currentTime lifeTime = match struct (startTime, currentTime, lifeTime) with - | struct (UpdateTime startTime, UpdateTime currentTime, UpdateTime lifeTime) -> (double (currentTime - startTime)) / double lifeTime |> max 0.0 |> min 1.0 - | struct (TickTime startTime, TickTime currentTime, TickTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> max 0.0 |> min 1.0 + | struct (UpdateTime startTime, UpdateTime currentTime, UpdateTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> single |> max 0.0f |> min 1.0f + | struct (TickTime startTime, TickTime currentTime, TickTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> single |> max 0.0f |> min 1.0f | struct (_, _, _) -> failwith "Cannot apply operation to mixed GameTimes." static member (+) (left, right) = GameTime.ap (+) (+) left right From 2c946331da10c8c8f5c9fad86a786014aab9f5c0 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 12 Nov 2025 00:27:18 -0500 Subject: [PATCH 29/52] Reverted change and added new GameTime.progressF for single-precision calculations. --- Nu/Nu/Core/GameTime.fs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Nu/Nu/Core/GameTime.fs b/Nu/Nu/Core/GameTime.fs index 5545e9a399..eafb40a9ea 100644 --- a/Nu/Nu/Core/GameTime.fs +++ b/Nu/Nu/Core/GameTime.fs @@ -144,13 +144,18 @@ and [ if leftTime < rightTime then -1 elif leftTime > rightTime then 1 else 0 | struct (_, _) -> failwith "Cannot apply operation to mixed GameTimes." - /// The progress of time down a unit-bounded range. + /// The progress of time down a unit-bounded range (double precision). static member progress startTime currentTime lifeTime = match struct (startTime, currentTime, lifeTime) with - | struct (UpdateTime startTime, UpdateTime currentTime, UpdateTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> single |> max 0.0f |> min 1.0f - | struct (TickTime startTime, TickTime currentTime, TickTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> single |> max 0.0f |> min 1.0f + | struct (UpdateTime startTime, UpdateTime currentTime, UpdateTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> max 0.0 |> min 1.0 + | struct (TickTime startTime, TickTime currentTime, TickTime lifeTime) -> double (currentTime - startTime) / double lifeTime |> max 0.0 |> min 1.0 | struct (_, _, _) -> failwith "Cannot apply operation to mixed GameTimes." + /// The progress of time down a unit-bounded range (single precision). + /// NOTE: this loses some very minor precision but is fine for casual use. + static member progressF startTime currentTime lifeTime = + single (GameTime.progress startTime currentTime lifeTime) + static member (+) (left, right) = GameTime.ap (+) (+) left right static member (-) (left, right) = GameTime.ap (-) (-) left right static member (*) (left, right) = GameTime.ap (*) (fun left right -> (left / Stopwatch.Frequency) * (right / Stopwatch.Frequency) / Stopwatch.Frequency |> int64) left right @@ -174,7 +179,7 @@ and [ Date: Wed, 12 Nov 2025 06:47:53 -0500 Subject: [PATCH 30/52] Implemented #751. --- Nu/Nu.Math/Box2.cs | 15 +++ Nu/Nu.Math/Box2i.cs | 15 +++ Nu/Nu.Math/Box3.cs | 16 +++ Nu/Nu.Math/Box3i.cs | 16 +++ Nu/Nu.Math/Vector2i.cs | 8 +- Nu/Nu.Math/Vector3i.cs | 8 +- Nu/Nu.Math/Vector4i.cs | 8 +- Nu/Nu/Assimp/Assimp.fs | 3 +- Nu/Nu/Metadata/HeightMap.fs | 26 ++--- Nu/Nu/World/WorldDispatchers.fs | 6 +- Nu/Nu/World/WorldEntityHierarchy.fs | 4 +- Nu/Nu/World/WorldFacets.fs | 21 ++-- Nu/Nu/World/WorldScreen.fs | 107 +++++++++++------- Nu/Nu/World/WorldTypes.fs | 10 +- .../Terra Firma/Assets/Gameplay/Scene.nugroup | 16 --- 15 files changed, 180 insertions(+), 99 deletions(-) diff --git a/Nu/Nu.Math/Box2.cs b/Nu/Nu.Math/Box2.cs index adca71c904..611dfbc210 100644 --- a/Nu/Nu.Math/Box2.cs +++ b/Nu/Nu.Math/Box2.cs @@ -375,5 +375,20 @@ public readonly void Intersects(in Box2 box, out bool result) min.X > max2.X || min.Y > max2.Y); } + + /// + /// Clips this to the given . + /// + /// The bounds to clip to. + /// The clipped . + public readonly Box2 Clip(Box2 bounds) + { + var min = Vector2.Max(Min, bounds.Min); + var max = Vector2.Min(Min + Size, bounds.Min + bounds.Size); + var newSize = max - min; + newSize.X = System.Math.Max(0.0f, newSize.X); + newSize.Y = System.Math.Max(0.0f, newSize.Y); + return new Box2(min, newSize); + } } } diff --git a/Nu/Nu.Math/Box2i.cs b/Nu/Nu.Math/Box2i.cs index f6621f21ba..9542553591 100644 --- a/Nu/Nu.Math/Box2i.cs +++ b/Nu/Nu.Math/Box2i.cs @@ -205,5 +205,20 @@ public override readonly string ToString() { return $"{{Min:{Min} Size:{Size}}}"; } + + /// + /// Clips this to the given . + /// + /// The bounds to clip to. + /// The clipped . + public readonly Box2i Clip(Box2i bounds) + { + var min = Vector2i.Max(Min, bounds.Min); + var max = Vector2i.Min(Min + Size, bounds.Min + bounds.Size); + var newSize = max - min; + newSize.X = System.Math.Max(0, newSize.X); + newSize.Y = System.Math.Max(0, newSize.Y); + return new Box2i(min, newSize); + } } } diff --git a/Nu/Nu.Math/Box3.cs b/Nu/Nu.Math/Box3.cs index d8c5dd1bd6..e3501f8f65 100644 --- a/Nu/Nu.Math/Box3.cs +++ b/Nu/Nu.Math/Box3.cs @@ -673,5 +673,21 @@ public readonly void Intersects(in Plane3 plane, out PlaneIntersectionType resul result = PlaneIntersectionType.Intersecting; } + + /// + /// Clips this to the given . + /// + /// The bounds to clip to. + /// The clipped . + public readonly Box3 Clip(Box3 bounds) + { + var min = Vector3.Max(Min, bounds.Min); + var max = Vector3.Min(Min + Size, bounds.Min + bounds.Size); + var newSize = max - min; + newSize.X = System.Math.Max(0.0f, newSize.X); + newSize.Y = System.Math.Max(0.0f, newSize.Y); + newSize.Z = System.Math.Max(0.0f, newSize.Z); + return new Box3(min, newSize); + } } } diff --git a/Nu/Nu.Math/Box3i.cs b/Nu/Nu.Math/Box3i.cs index 90cccac060..e70f617526 100644 --- a/Nu/Nu.Math/Box3i.cs +++ b/Nu/Nu.Math/Box3i.cs @@ -429,5 +429,21 @@ public readonly void Intersects(in Box3i box, out bool result) min.Y > max2.Y || min.Z > max2.Z); } + + /// + /// Clips this to the given . + /// + /// The bounds to clip to. + /// The clipped . + public readonly Box3i Clip(Box3i bounds) + { + var min = Vector3i.Max(Min, bounds.Min); + var max = Vector3i.Min(Min + Size, bounds.Min + bounds.Size); + var newSize = max - min; + newSize.X = System.Math.Max(0, newSize.X); + newSize.Y = System.Math.Max(0, newSize.Y); + newSize.Z = System.Math.Max(0, newSize.Z); + return new Box3i(min, newSize); + } } } diff --git a/Nu/Nu.Math/Vector2i.cs b/Nu/Nu.Math/Vector2i.cs index fd4b75ab3e..9295882924 100644 --- a/Nu/Nu.Math/Vector2i.cs +++ b/Nu/Nu.Math/Vector2i.cs @@ -277,7 +277,7 @@ public static void Divide(in Vector2i vector, in Vector2i scale, out Vector2i re /// First operand. /// Second operand. /// The component-wise minimum. - public static Vector2i ComponentMin(Vector2i a, Vector2i b) + public static Vector2i Min(Vector2i a, Vector2i b) { a.X = a.X < b.X ? a.X : b.X; a.Y = a.Y < b.Y ? a.Y : b.Y; @@ -290,7 +290,7 @@ public static Vector2i ComponentMin(Vector2i a, Vector2i b) /// First operand. /// Second operand. /// The component-wise minimum. - public static void ComponentMin(in Vector2i a, in Vector2i b, out Vector2i result) + public static void Min(in Vector2i a, in Vector2i b, out Vector2i result) { result.X = a.X < b.X ? a.X : b.X; result.Y = a.Y < b.Y ? a.Y : b.Y; @@ -302,7 +302,7 @@ public static void ComponentMin(in Vector2i a, in Vector2i b, out Vector2i resul /// First operand. /// Second operand. /// The component-wise maximum. - public static Vector2i ComponentMax(Vector2i a, Vector2i b) + public static Vector2i Max(Vector2i a, Vector2i b) { a.X = a.X > b.X ? a.X : b.X; a.Y = a.Y > b.Y ? a.Y : b.Y; @@ -315,7 +315,7 @@ public static Vector2i ComponentMax(Vector2i a, Vector2i b) /// First operand. /// Second operand. /// The component-wise maximum. - public static void ComponentMax(in Vector2i a, in Vector2i b, out Vector2i result) + public static void Max(in Vector2i a, in Vector2i b, out Vector2i result) { result.X = a.X > b.X ? a.X : b.X; result.Y = a.Y > b.Y ? a.Y : b.Y; diff --git a/Nu/Nu.Math/Vector3i.cs b/Nu/Nu.Math/Vector3i.cs index 52078ca3b0..3f01263f6a 100644 --- a/Nu/Nu.Math/Vector3i.cs +++ b/Nu/Nu.Math/Vector3i.cs @@ -278,7 +278,7 @@ public static void Divide(in Vector3i vector, in Vector3i scale, out Vector3i re /// First operand. /// Second operand. /// The component-wise minimum. - public static Vector3i ComponentMin(Vector3i a, Vector3i b) + public static Vector3i Min(Vector3i a, Vector3i b) { a.X = a.X < b.X ? a.X : b.X; a.Y = a.Y < b.Y ? a.Y : b.Y; @@ -292,7 +292,7 @@ public static Vector3i ComponentMin(Vector3i a, Vector3i b) /// First operand. /// Second operand. /// The component-wise minimum. - public static void ComponentMin(in Vector3i a, in Vector3i b, out Vector3i result) + public static void Min(in Vector3i a, in Vector3i b, out Vector3i result) { result.X = a.X < b.X ? a.X : b.X; result.Y = a.Y < b.Y ? a.Y : b.Y; @@ -305,7 +305,7 @@ public static void ComponentMin(in Vector3i a, in Vector3i b, out Vector3i resul /// First operand. /// Second operand. /// The component-wise maximum. - public static Vector3i ComponentMax(Vector3i a, Vector3i b) + public static Vector3i Max(Vector3i a, Vector3i b) { a.X = a.X > b.X ? a.X : b.X; a.Y = a.Y > b.Y ? a.Y : b.Y; @@ -319,7 +319,7 @@ public static Vector3i ComponentMax(Vector3i a, Vector3i b) /// First operand. /// Second operand. /// The component-wise maximum. - public static void ComponentMax(in Vector3i a, in Vector3i b, out Vector3i result) + public static void Max(in Vector3i a, in Vector3i b, out Vector3i result) { result.X = a.X > b.X ? a.X : b.X; result.Y = a.Y > b.Y ? a.Y : b.Y; diff --git a/Nu/Nu.Math/Vector4i.cs b/Nu/Nu.Math/Vector4i.cs index 522a6522c3..3f7c129002 100644 --- a/Nu/Nu.Math/Vector4i.cs +++ b/Nu/Nu.Math/Vector4i.cs @@ -299,7 +299,7 @@ public static void Divide(in Vector4i vector, in Vector4i scale, out Vector4i re /// First operand. /// Second operand. /// The component-wise minimum. - public static Vector4i ComponentMin(Vector4i a, Vector4i b) + public static Vector4i Min(Vector4i a, Vector4i b) { a.X = a.X < b.X ? a.X : b.X; a.Y = a.Y < b.Y ? a.Y : b.Y; @@ -314,7 +314,7 @@ public static Vector4i ComponentMin(Vector4i a, Vector4i b) /// First operand. /// Second operand. /// The component-wise minimum. - public static void ComponentMin(in Vector4i a, in Vector4i b, out Vector4i result) + public static void Min(in Vector4i a, in Vector4i b, out Vector4i result) { result.X = a.X < b.X ? a.X : b.X; result.Y = a.Y < b.Y ? a.Y : b.Y; @@ -328,7 +328,7 @@ public static void ComponentMin(in Vector4i a, in Vector4i b, out Vector4i resul /// First operand. /// Second operand. /// The component-wise maximum. - public static Vector4i ComponentMax(Vector4i a, Vector4i b) + public static Vector4i Max(Vector4i a, Vector4i b) { a.X = a.X > b.X ? a.X : b.X; a.Y = a.Y > b.Y ? a.Y : b.Y; @@ -343,7 +343,7 @@ public static Vector4i ComponentMax(Vector4i a, Vector4i b) /// First operand. /// Second operand. /// The component-wise maximum. - public static void ComponentMax(in Vector4i a, in Vector4i b, out Vector4i result) + public static void Max(in Vector4i a, in Vector4i b, out Vector4i result) { result.X = a.X > b.X ? a.X : b.X; result.Y = a.Y > b.Y ? a.Y : b.Y; diff --git a/Nu/Nu/Assimp/Assimp.fs b/Nu/Nu/Assimp/Assimp.fs index 88075f0fc6..01a6df277e 100644 --- a/Nu/Nu/Assimp/Assimp.fs +++ b/Nu/Nu/Assimp/Assimp.fs @@ -66,8 +66,7 @@ type RenderStyle = type NavShape = | EmptyNavShape | BoundsNavShape - | StaticModelNavShape - | StaticModelSurfaceNavShape + | ContourNavShape /// The batch phasing such involved in persisting OpenGL state. type [] BatchPhase = diff --git a/Nu/Nu/Metadata/HeightMap.fs b/Nu/Nu/Metadata/HeightMap.fs index 6124752bcc..ff5e9fdf1f 100644 --- a/Nu/Nu/Metadata/HeightMap.fs +++ b/Nu/Nu/Metadata/HeightMap.fs @@ -37,8 +37,8 @@ type [] HeightMap = | ImageHeightMap of Image AssetTag // only supports 8-bit depth on Red channel | RawHeightMap of RawHeightMap - static member private tryGetTextureData tryGetAssetFilePath (assetTag : Image AssetTag) = - match tryGetAssetFilePath assetTag with + static member private tryGetTextureData tryGetFilePath (assetTag : Image AssetTag) = + match tryGetFilePath assetTag with | Some filePath -> match OpenGL.Texture.TryCreateTextureData (false, filePath) with | Some textureData -> @@ -49,8 +49,8 @@ type [] HeightMap = | None -> ValueNone | None -> ValueNone - static member private tryGetRawAssetData tryGetAssetFilePath (assetTag : Raw AssetTag) = - match tryGetAssetFilePath assetTag with + static member private tryGetRawAssetData tryGetFilePath (assetTag : Raw AssetTag) = + match tryGetFilePath assetTag with | Some filePath -> try let bytes = File.ReadAllBytes filePath ValueSome bytes @@ -59,16 +59,16 @@ type [] HeightMap = ValueNone | None -> ValueNone - static member private tryGetImageHeightMapMetadata tryGetAssetFilePath (bounds : Box3) tiles image = + static member private tryGetImageHeightMapMetadata tryGetFilePath (bounds : Box3) tiles image = // attempt to load texture data - match HeightMap.tryGetTextureData tryGetAssetFilePath image with + match HeightMap.tryGetTextureData tryGetFilePath image with | ValueSome (metadata, compressed, bytes) -> // currently only supporting height data from block-compressed files if not compressed then - // compute normalize heights + // compute normalized heights let resolutionX = metadata.TextureWidth let resolutionY = metadata.TextureHeight let scalar = 1.0f / single Byte.MaxValue @@ -76,7 +76,7 @@ type [] HeightMap = [|for y in 0 .. dec resolutionY do for x in 0 .. dec resolutionX do let index = (resolutionX * y + x) * 4 + 2 // extract r channel of pixel - single bytes[index] * scalar|] + single bytes.[index] * scalar|] // compute positions and tex coordses let quadSizeX = bounds.Size.X / single (dec resolutionX) @@ -100,10 +100,10 @@ type [] HeightMap = else Log.info "Block-compressed image files are unsupported for use as height maps."; ValueNone | ValueNone -> ValueNone - static member private tryGetRawHeightMapMetadata tryGetAssetFilePath (bounds : Box3) tiles map = + static member private tryGetRawHeightMapMetadata tryGetFilePath (bounds : Box3) tiles map = // ensure raw asset exists - match HeightMap.tryGetRawAssetData tryGetAssetFilePath map.RawAsset with + match HeightMap.tryGetRawAssetData tryGetFilePath map.RawAsset with | ValueSome rawAsset -> try // read normalized heights @@ -168,7 +168,7 @@ type [] HeightMap = /// produced here is slightly different, with the border slightly clipped, and the terrain and quad size, slightly /// larger. i.e if the original map is 32m^2 and the original quad 1m^2 and the heightmap is 32x32, the quad axes /// below will be > 1.0. - static member tryGetMetadata (tryGetAssetFilePath : AssetTag -> string option) bounds tiles heightMap = + static member tryGetMetadata (tryGetFilePath : AssetTag -> string option) bounds tiles heightMap = match heightMap with - | ImageHeightMap image -> HeightMap.tryGetImageHeightMapMetadata tryGetAssetFilePath bounds tiles image - | RawHeightMap map -> HeightMap.tryGetRawHeightMapMetadata tryGetAssetFilePath bounds tiles map \ No newline at end of file + | ImageHeightMap image -> HeightMap.tryGetImageHeightMapMetadata tryGetFilePath bounds tiles image + | RawHeightMap map -> HeightMap.tryGetRawHeightMapMetadata tryGetFilePath bounds tiles map \ No newline at end of file diff --git a/Nu/Nu/World/WorldDispatchers.fs b/Nu/Nu/World/WorldDispatchers.fs index 503a96e924..cd1aebfaab 100644 --- a/Nu/Nu/World/WorldDispatchers.fs +++ b/Nu/Nu/World/WorldDispatchers.fs @@ -585,7 +585,7 @@ type RigidModelDispatcher () = let entity = evt.Subscriber : Entity match entity.GetBodyType world with | Static -> entity.SetNavShape BoundsNavShape world - | Kinematic | KinematicCharacter | Dynamic | DynamicCharacter | Vehicle -> entity.SetNavShape NavShape.EmptyNavShape world + | Kinematic | KinematicCharacter | Dynamic | DynamicCharacter | Vehicle -> entity.SetNavShape EmptyNavShape world Cascade static member Facets = @@ -595,7 +595,7 @@ type RigidModelDispatcher () = static member Properties = [define Entity.BodyShape (StaticModelShape { StaticModel = Assets.Default.StaticModel; Profile = Convex; TransformOpt = None; PropertiesOpt = None }) - define Entity.NavShape StaticModelNavShape] + define Entity.NavShape ContourNavShape] override this.Register (entity, world) = World.monitor updateBodyShape entity.StaticModel.ChangeEvent entity world @@ -674,7 +674,7 @@ type RigidModelSurfaceDispatcher () = static member Properties = [define Entity.BodyShape (StaticModelSurfaceShape { StaticModel = Assets.Default.StaticModel; SurfaceIndex = 0; Profile = Convex; TransformOpt = None; PropertiesOpt = None }) - define Entity.NavShape StaticModelSurfaceNavShape] + define Entity.NavShape ContourNavShape] override this.Register (entity, world) = World.monitor updateBodyShape entity.StaticModel.ChangeEvent entity world diff --git a/Nu/Nu/World/WorldEntityHierarchy.fs b/Nu/Nu/World/WorldEntityHierarchy.fs index 48b7fc495e..58cf6516dd 100644 --- a/Nu/Nu/World/WorldEntityHierarchy.fs +++ b/Nu/Nu/World/WorldEntityHierarchy.fs @@ -96,7 +96,7 @@ module WorldEntityHierarchyExtensions = //let concave = OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.extractConcave concave staticModelMetadata.SceneOpt surface let surfaceShape = { surfaceShape with Profile = profile } child.SetBodyShape (StaticModelSurfaceShape surfaceShape) world - let navShape = OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.extractNavShape StaticModelSurfaceNavShape staticModelMetadata.SceneOpt surface + let navShape = OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.extractNavShape ContourNavShape staticModelMetadata.SceneOpt surface child.SetNavShape navShape world child else World.createEntity mountOpt DefaultOverlay (Some surnames) group world @@ -328,7 +328,7 @@ module Permafreezer3dDispatcherExtensions = let frozenShapes = getFrozenShapes this world for (bounds, matrix, staticModel, surfaceIndex, navShape, _, _) in frozenShapes do let navId = { NavIndex = index; NavEntity = this } - World.setNav3dBodyOpt (Some (bounds, matrix, staticModel, surfaceIndex, navShape)) navId world + World.setNav3dBodyOpt (Some (bounds, matrix, StaticModelSurfaceNavBody (staticModel, surfaceIndex), navShape)) navId world index <- inc index member internal this.RegisterFrozenShapesPhysics getFrozenShapes world = diff --git a/Nu/Nu/World/WorldFacets.fs b/Nu/Nu/World/WorldFacets.fs index 897b03b242..e24e3b0d81 100644 --- a/Nu/Nu/World/WorldFacets.fs +++ b/Nu/Nu/World/WorldFacets.fs @@ -3942,7 +3942,7 @@ type NavBodyFacet () = static let propagateNavBody (entity : Entity) world = let navId = { NavIndex = -1; NavEntity = entity } match entity.GetNavShape world with - | NavShape.EmptyNavShape -> + | EmptyNavShape -> if entity.GetIs2d world then () // TODO: implement for 2d navigation when it's available. else World.setNav3dBodyOpt None navId world @@ -3953,15 +3953,19 @@ type NavBodyFacet () = if entity.GetNavEnabled world then let bounds = entity.GetBounds world let affineMatrix = entity.GetAffineMatrix world - let staticModel = entity.GetStaticModel world - let surfaceIndex = entity.GetSurfaceIndex world - World.setNav3dBodyOpt (Some (bounds, affineMatrix, staticModel, surfaceIndex, shape)) navId world + match (entity.TryGet (nameof Entity.StaticModel) world, entity.TryGet (nameof Entity.SurfaceIndex) world) with + | (ValueSome staticModel, ValueNone) -> + World.setNav3dBodyOpt (Some (bounds, affineMatrix, StaticModelNavBody staticModel, shape)) navId world + | (ValueSome staticModel, ValueSome surfaceIndex) -> + World.setNav3dBodyOpt (Some (bounds, affineMatrix, StaticModelSurfaceNavBody (staticModel, surfaceIndex), shape)) navId world + | (_, _) -> + match entity.TryGet (nameof Entity.HeightMap) world with + | ValueSome heightMap -> World.setNav3dBodyOpt (Some (bounds, affineMatrix, HeightMapNavBody heightMap, shape)) navId world + | ValueNone -> World.setNav3dBodyOpt None navId world else World.setNav3dBodyOpt None navId world static member Properties = - [define Entity.StaticModel Assets.Default.StaticModel - define Entity.SurfaceIndex 0 - define Entity.NavShape BoundsNavShape + [define Entity.NavShape ContourNavShape define Entity.NavEnabled true] override this.Register (entity, world) = @@ -3997,7 +4001,7 @@ type NavBodyFacet () = Cascade let callback4 _ world = unsubscribe world; Cascade match entity.GetNavShape world with - | NavShape.EmptyNavShape -> () + | EmptyNavShape -> () | _ -> subscribe world World.sense callback (entity.ChangeEvent (nameof entity.NavShape)) entity (nameof NavBodyFacet) world World.sense callback2 (entity.ChangeEvent (nameof entity.NavEnabled)) entity (nameof NavBodyFacet) world @@ -4008,6 +4012,7 @@ type NavBodyFacet () = let callbackPnb evt world = propagateNavBody evt.Subscriber world; Cascade World.sense callbackPnb (entity.ChangeEvent (nameof entity.StaticModel)) entity (nameof NavBodyFacet) world World.sense callbackPnb (entity.ChangeEvent (nameof entity.SurfaceIndex)) entity (nameof NavBodyFacet) world + World.sense callbackPnb (entity.ChangeEvent (nameof entity.HeightMap)) entity (nameof NavBodyFacet) world propagateNavBody entity world override this.Unregister (entity, world) = diff --git a/Nu/Nu/World/WorldScreen.fs b/Nu/Nu/World/WorldScreen.fs index 243d8889b2..5ba6fd2e02 100644 --- a/Nu/Nu/World/WorldScreen.fs +++ b/Nu/Nu/World/WorldScreen.fs @@ -341,28 +341,34 @@ module WorldScreenModule = setScreenSlide slideDescriptor destination screen world static member internal getNav3dDescriptors contents = - [for (bounds : Box3, affineMatrix, staticModel, surfaceIndex, content) in contents do + [for (bounds : Box3, affineMatrix : Matrix4x4, nav3dBody, content) in contents do match content with - | NavShape.EmptyNavShape -> () - | NavShape.BoundsNavShape -> Left bounds - | NavShape.StaticModelSurfaceNavShape -> - match Metadata.tryGetStaticModelMetadata staticModel with - | ValueSome physicallyBasedModel -> - if surfaceIndex >= 0 && surfaceIndex < physicallyBasedModel.Surfaces.Length then - if bounds.Size.Magnitude < Constants.Nav.Bounds3dMagnitudeMax then - Right (bounds, affineMatrix, physicallyBasedModel.Surfaces.[surfaceIndex]) - else - Log.warn "Navigation shape bounds magnitude exceeded maximum; ignoring." - | ValueNone -> () - | NavShape.StaticModelNavShape -> - match Metadata.tryGetStaticModelMetadata staticModel with - | ValueSome physicallyBasedModel -> - for surface in physicallyBasedModel.Surfaces do - if bounds.Size.Magnitude < Constants.Nav.Bounds3dMagnitudeMax then - Right (bounds, affineMatrix, surface) - else - Log.warn "Navigation shape bounds magnitude exceeded maximum; ignoring." - | ValueNone -> ()] + | EmptyNavShape -> () + | BoundsNavShape -> Choice1Of3 bounds + | ContourNavShape -> + match nav3dBody with + | StaticModelNavBody staticModel -> + match Metadata.tryGetStaticModelMetadata staticModel with + | ValueSome physicallyBasedModel -> + for surface in physicallyBasedModel.Surfaces do + if bounds.Size.Magnitude < Constants.Nav.Bounds3dMagnitudeMax then + Choice2Of3 (bounds, affineMatrix, surface) + else + Log.warn "Navigation shape bounds magnitude exceeded maximum; ignoring." + | ValueNone -> () + | StaticModelSurfaceNavBody (staticModel, surfaceIndex) -> + match Metadata.tryGetStaticModelMetadata staticModel with + | ValueSome physicallyBasedModel -> + if surfaceIndex >= 0 && surfaceIndex < physicallyBasedModel.Surfaces.Length then + if bounds.Size.Magnitude < Constants.Nav.Bounds3dMagnitudeMax then + Choice2Of3 (bounds, affineMatrix, physicallyBasedModel.Surfaces.[surfaceIndex]) + else + Log.warn "Navigation shape bounds magnitude exceeded maximum; ignoring." + | ValueNone -> () + | HeightMapNavBody heightMap -> + match HeightMap.tryGetMetadata Metadata.tryGetFilePath bounds v2One heightMap with + | ValueSome heightMap -> Choice3Of3 (bounds, heightMap) + | ValueNone -> ()] static member internal trySaveNav3dMesh (navBuilderResultData : NavBuilderResultData) dtNavMesh filePathOpt = try match filePathOpt with @@ -392,14 +398,14 @@ module WorldScreenModule = let vertices = [|for descriptor in descriptors do match descriptor with - | Left bounds -> + | Choice1Of3 bounds -> match boundsOpt with | None -> boundsOpt <- Some bounds | Some (bounds' : Box3) -> boundsOpt <- Some (bounds'.Combine bounds) let corners = bounds.Corners for corner in corners do corner.X; corner.Y; corner.Z - | Right (bounds, affineMatrix : Matrix4x4, surface) -> + | Choice2Of3 (bounds, affineMatrix : Matrix4x4, surface) -> let geometry = surface.PhysicallyBasedGeometry match boundsOpt with | None -> boundsOpt <- Some bounds @@ -407,14 +413,20 @@ module WorldScreenModule = if geometry.PrimitiveType = OpenGL.PrimitiveType.Triangles then for v in geometry.Vertices do let v' = v.Transform affineMatrix - v'.X; v'.Y; v'.Z|] + v'.X; v'.Y; v'.Z + | Choice3Of3 (bounds, heightMap) -> + match boundsOpt with + | None -> boundsOpt <- Some bounds + | Some (bounds' : Box3) -> boundsOpt <- Some (bounds'.Combine bounds) + for struct (p, _) in heightMap.PositionsAndTexCoordses do + p.X; p.Y; p.Z|] // compute indices let mutable offset = 0 let indices = [|for descriptor in descriptors do match descriptor with - | Left _ -> + | Choice1Of3 _ -> // as the corners are ordered in Box3.Corners... // // 6--------7 @@ -426,30 +438,43 @@ module WorldScreenModule = // |/ |/ // 1--------2 // - offset + 2; offset + 3; offset + 7 // right - offset + 2; offset + 7; offset + 4 - offset + 0; offset + 1; offset + 5 // left - offset + 0; offset + 5; offset + 6 - offset + 4; offset + 5; offset + 6 // top - offset + 4; offset + 6; offset + 7 - offset + 0; offset + 1; offset + 2 // bottom - offset + 0; offset + 2; offset + 3 - offset + 0; offset + 3; offset + 7 // back - offset + 0; offset + 7; offset + 6 - offset + 1; offset + 2; offset + 4 // front - offset + 1; offset + 4; offset + 5 + yield offset + 2; yield offset + 3; yield offset + 7 // right + yield offset + 2; yield offset + 7; yield offset + 4 + yield offset + 0; yield offset + 1; yield offset + 5 // left + yield offset + 0; yield offset + 5; yield offset + 6 + yield offset + 4; yield offset + 5; yield offset + 6 // top + yield offset + 4; yield offset + 6; yield offset + 7 + yield offset + 0; yield offset + 1; yield offset + 2 // bottom + yield offset + 0; yield offset + 2; yield offset + 3 + yield offset + 0; yield offset + 3; yield offset + 7 // back + yield offset + 0; yield offset + 7; yield offset + 6 + yield offset + 1; yield offset + 2; yield offset + 4 // front + yield offset + 1; yield offset + 4; yield offset + 5 offset <- offset + 8 - | Right (_, _, surface) -> + | Choice2Of3 (_, _, surface) -> let geometry = surface.PhysicallyBasedGeometry if geometry.PrimitiveType = OpenGL.PrimitiveType.Triangles then for i in geometry.Indices do - i + offset - offset <- offset + geometry.Vertices.Length|] + yield offset + i + offset <- offset + geometry.Vertices.Length + | Choice3Of3 (_, heightMap) -> + for y in 0 .. dec heightMap.Resolution.Y - 1 do + for x in 0 .. dec heightMap.Resolution.X - 1 do + yield offset + heightMap.Resolution.X * y + x + yield offset + heightMap.Resolution.X * inc y + x + yield offset + heightMap.Resolution.X * y + inc x + yield offset + heightMap.Resolution.X * inc y + x + yield offset + heightMap.Resolution.X * inc y + inc x + yield offset + heightMap.Resolution.X * y + inc x + offset <- offset + heightMap.PositionsAndTexCoordses.Length|] // attempt to create geometry provider match boundsOpt with | Some bounds when vertices.Length >= 3 && indices.Length >= 3 -> - let provider = Nav3dInputGeomProvider (vertices, indices, bounds) + let boundsBoundsSize = v3Dup Constants.Nav.Bounds3dMagnitudeMax + let boundsBounds = box3 (bounds.Center - boundsBoundsSize * 0.5f) boundsBoundsSize + let boundsClipped = bounds.Clip boundsBounds + let provider = Nav3dInputGeomProvider (vertices, indices, boundsClipped) Some (provider :> IInputGeomProvider) | Some _ | None -> None diff --git a/Nu/Nu/World/WorldTypes.fs b/Nu/Nu/World/WorldTypes.fs index 917109ce4f..4a817c809f 100644 --- a/Nu/Nu/World/WorldTypes.fs +++ b/Nu/Nu/World/WorldTypes.fs @@ -272,12 +272,18 @@ and NavId = { NavEntity : Entity NavIndex : int } +/// Describes a navigable 3D body. +and Nav3dBody = + | StaticModelNavBody of StaticModel AssetTag + | StaticModelSurfaceNavBody of StaticModel AssetTag * int + | HeightMapNavBody of HeightMap + /// Represents 3d navigation capabilies for a screen. /// NOTE: this type is intended only for internal engine use. and [] Nav3d = { Nav3dContext : RcContext - Nav3dBodies : Map - Nav3dBodiesOldOpt : Map option + Nav3dBodies : Map + Nav3dBodiesOldOpt : Map option Nav3dConfig : Nav3dConfig Nav3dConfigOldOpt : Nav3dConfig option Nav3dMeshOpt : (string option * NavBuilderResultData * DtNavMesh * DtNavMeshQuery) option } diff --git a/Projects/Terra Firma/Assets/Gameplay/Scene.nugroup b/Projects/Terra Firma/Assets/Gameplay/Scene.nugroup index 618cfcc7ba..aa167943e1 100644 --- a/Projects/Terra Firma/Assets/Gameplay/Scene.nugroup +++ b/Projects/Terra Firma/Assets/Gameplay/Scene.nugroup @@ -171,7 +171,6 @@ [FinenessOffsetOpt None] [ScatterTypeOpt None]]] [Name Ground] - [NavShape StaticModelNavShape] [Order 3559661103553] [Position [-2 1.59 -4.5]] [PositionLocal [-2 -0.05 -4.5]] @@ -234,7 +233,6 @@ [[Gameplay House] Concave None None]]] [MountOpt [Some ^]] [Name House] - [NavShape StaticModelNavShape] [Offset [0 0.3668699 0]] [Order 1645309435779] [Position [-0.5 1.64 -4.7]] @@ -309,7 +307,6 @@ [OpaqueDistanceOpt None]]] [MountOpt [Some ^]] [Name House2] - [NavShape StaticModelNavShape] [Offset [0 0.6676477 0]] [Order 613197939149] [Position [-6.7 1.64 2.6]] @@ -614,7 +611,6 @@ [[Gameplay Barrier] Convex None None]]] [MountOpt [Some ^]] [Name Barrier] - [NavShape StaticModelNavShape] [Offset [0.110984206 0.46470478 0.8669058]] [Order 1645316460604] [Position [-8.2 1.6 7]] @@ -1254,7 +1250,6 @@ [TwoSidedOpt None] [ClippedOpt None]]] [Name Container] - [NavShape StaticModelNavShape] [Offset [1.1920929E-07 -0.07810056 -2.3841858E-07]] [Order 3559665458680] [Position [0 3 -14.8]] @@ -1344,7 +1339,6 @@ [RefractiveIndexOpt None]]] [MountOpt [Some ^]] [Name Ground] - [NavShape StaticModelNavShape] [Position [-2 1.59 -4.5]] [PositionLocal [-2 -0.05 -4.5]] [Scale [26.9 0.1 30.4]] @@ -1408,7 +1402,6 @@ [[Gameplay House] Concave None None]]] [MountOpt [Some ^]] [Name House] - [NavShape StaticModelNavShape] [Offset [0 0.3668699 0]] [Order 1645309435779] [Position [-0.5 1.64 -4.7]] @@ -1485,7 +1478,6 @@ [OpaqueDistanceOpt None]]] [MountOpt [Some ^]] [Name House2] - [NavShape StaticModelNavShape] [Offset [0 0.6676477 0]] [Order 613197939149] [Position [-6.7 1.64 2.6]] @@ -1806,7 +1798,6 @@ [[Gameplay Barrier] Convex None None]]] [MountOpt [Some ^]] [Name Barrier] - [NavShape StaticModelNavShape] [Offset [0.110984206 0.46470478 0.8669058]] [Order 1645316460604] [Position [-8.2 1.6 7]] @@ -2467,7 +2458,6 @@ [ClippedOpt None]]] [MountOpt [Some ^]] [Name Container] - [NavShape StaticModelNavShape] [Offset [1.1920929E-07 -0.07810056 -2.3841858E-07]] [Position [0 3 -14.8]] [PositionLocal [0 1.36 -14.8]] @@ -2562,7 +2552,6 @@ [RefractiveIndexOpt None]]] [MountOpt [Some ^]] [Name Ground] - [NavShape StaticModelNavShape] [Position [-3.7000003 1.25 25.5]] [PositionLocal [-2 -0.05 -4.5]] [Rotation [0 -0.70710677 0 0.70710677]] @@ -3520,7 +3509,6 @@ [ClippedOpt None]]] [MountOpt [Some ^]] [Name Container] - [NavShape StaticModelNavShape] [Offset [1.1920929E-07 -0.0909127 -2.3841858E-07]] [Position [6.5999994 2.6599998 27.5]] [PositionLocal [0 1.36 -14.8]] @@ -3601,7 +3589,6 @@ [RefractiveIndexOpt None]]] [MountOpt [Some ^]] [Name Ground] - [NavShape StaticModelNavShape] [Position [26.7 2.05 27.2]] [PositionLocal [-2 -0.05 -4.5]] [Rotation [0 -1 0 -4.371139E-08]] @@ -4559,7 +4546,6 @@ [ClippedOpt None]]] [MountOpt [Some ^]] [Name Container] - [NavShape StaticModelNavShape] [Offset [1.1920929E-07 -0.07810056 -2.3841858E-07]] [Position [24.699999 3.46 37.5]] [PositionLocal [0 1.36 -14.8]] @@ -4640,7 +4626,6 @@ [RefractiveIndexOpt None]]] [MountOpt [Some ^]] [Name Ground] - [NavShape StaticModelNavShape] [Position [26.8 2.05 -4.5]] [PositionLocal [-2 -0.05 -4.5]] [Rotation [0 1 -0 -4.371139E-08]] @@ -5598,7 +5583,6 @@ [ClippedOpt None]]] [MountOpt [Some ^]] [Name Container] - [NavShape StaticModelNavShape] [Offset [1.1920929E-07 -0.07810056 -2.3841858E-07]] [Position [24.800001 3.46 5.8]] [PositionLocal [0 1.36 -14.8]] From 7ab0125a07389d975b239c56d5f5ba1dba2ed334 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 12 Nov 2025 13:02:05 -0500 Subject: [PATCH 31/52] Fixed unnecessary exceptions. Removed dead TODOs. --- Nu/Nu/World/WorldModuleEntity.fs | 63 ++++++++++++++++++-------------- Nu/Nu/World/WorldModuleGame.fs | 50 +++++++++++++------------ Nu/Nu/World/WorldModuleGroup.fs | 50 +++++++++++++------------ Nu/Nu/World/WorldModuleScreen.fs | 50 +++++++++++++------------ 4 files changed, 116 insertions(+), 97 deletions(-) diff --git a/Nu/Nu/World/WorldModuleEntity.fs b/Nu/Nu/World/WorldModuleEntity.fs index fc3e25b5a8..6396880a4b 100644 --- a/Nu/Nu/World/WorldModuleEntity.fs +++ b/Nu/Nu/World/WorldModuleEntity.fs @@ -1753,7 +1753,7 @@ module WorldModuleEntity = else true else false - static member internal getEntityXtensionValue<'a> propertyName entity (world : World) = + static member internal tryGetEntityXtensionValueObj<'a> propertyName entity world : obj option = let entityStateOpt = World.getEntityStateOpt entity world match entityStateOpt :> obj with | null -> failwithf "Could not find entity '%s'." (scstring entity) @@ -1766,53 +1766,60 @@ module WorldModuleEntity = | :? ComputedProperty as cp -> cp.ComputedGet entity world | _ -> property.PropertyValue match valueObj with - | :? 'a as value -> value - | null -> null :> obj :?> 'a - | value -> - let value = - try value |> valueToSymbol |> symbolToValue + | :? 'a -> Some valueObj + | null -> null :> obj |> Some + | valueObj -> + let valueObj = + try valueObj |> valueToSymbol |> symbolToValue with _ -> - let value = typeof<'a>.GetDefaultValue () + let valueObj = typeof<'a>.GetDefaultValue () Log.warn "Could not gracefully promote value to the required type, so using a default value instead." - value :?> 'a + valueObj match property.PropertyValue with - | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- value + | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- valueObj | :? ComputedProperty -> () // nothing to do - | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- value - value + | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- valueObj + Some valueObj else - let value = + let valueObjOpt = match entityStateOpt.OverlayNameOpt with | Some overlayName -> match World.tryGetOverlayerPropertyValue propertyName typeof<'a> overlayName entityStateOpt.FacetNames world with - | Some value -> value :?> 'a + | Some value -> Some value | None -> let definitions = Reflection.getPropertyDefinitions (getType entityStateOpt.Dispatcher) match List.tryFind (fun (pd : PropertyDefinition) -> pd.PropertyName = propertyName) definitions with | Some definition -> match definition.PropertyExpr with - | DefineExpr value -> value :?> 'a - | VariableExpr eval -> eval world :?> 'a - | ComputedExpr property -> property.ComputedGet entity world :?> 'a - | None -> failwithumf () + | DefineExpr value -> Some value + | VariableExpr eval -> eval world |> Some + | ComputedExpr property -> property.ComputedGet entity world |> Some + | None -> None | None -> let definitions = Reflection.getPropertyDefinitions (getType entityStateOpt.Dispatcher) match List.tryFind (fun (pd : PropertyDefinition) -> pd.PropertyName = propertyName) definitions with | Some definition -> match definition.PropertyExpr with - | DefineExpr value -> value :?> 'a - | VariableExpr eval -> eval world :?> 'a - | ComputedExpr property -> property.ComputedGet entity world :?> 'a - | None -> failwithumf () - let property = { PropertyType = typeof<'a>; PropertyValue = value } - entityStateOpt.Xtension <- Xtension.attachProperty propertyName property entityStateOpt.Xtension - value + | DefineExpr value -> Some value + | VariableExpr eval -> eval world |> Some + | ComputedExpr property -> property.ComputedGet entity world |> Some + | None -> None + match valueObjOpt with + | Some valueObj -> + let property = { PropertyType = typeof<'a>; PropertyValue = valueObj } + entityStateOpt.Xtension <- Xtension.attachProperty propertyName property entityStateOpt.Xtension + Some valueObj + | None -> None static member internal tryGetEntityXtensionValue<'a> propertyName entity world : 'a voption = - // NOTE: we're only using exceptions as flow control in order to avoid code duplication and perf costs. - // TODO: P1: see if we can find a way to refactor this situation without incurring any additional overhead on the getEntityXtensionValue call. - try World.getEntityXtensionValue<'a> propertyName entity world |> ValueSome - with _ -> ValueNone + match World.tryGetEntityXtensionValueObj<'a> propertyName entity world with + | Some valueObj -> valueObj :?> 'a |> ValueSome + | None -> ValueNone + + static member internal getEntityXtensionValue<'a> propertyName entity (world : World) = + match World.tryGetEntityXtensionValueObj<'a> propertyName entity world with + | Some valueObj -> valueObj :?> 'a + | None -> failwithumf () static member internal getEntityProperty propertyName entity world = let mutable property = Unchecked.defaultof<_> diff --git a/Nu/Nu/World/WorldModuleGame.fs b/Nu/Nu/World/WorldModuleGame.fs index 59957d44d5..570b47b2ca 100644 --- a/Nu/Nu/World/WorldModuleGame.fs +++ b/Nu/Nu/World/WorldModuleGame.fs @@ -632,7 +632,7 @@ module WorldModuleGame = | _ -> true else false - static member internal getGameXtensionValue<'a> propertyName game world = + static member internal tryGetGameXtensionValueObj<'a> propertyName game world = let gameState = World.getGameState game world let mutable property = Unchecked.defaultof<_> if GameState.tryGetProperty (propertyName, gameState, &property) then @@ -642,39 +642,43 @@ module WorldModuleGame = | :? ComputedProperty as cp -> cp.ComputedGet game world | _ -> property.PropertyValue match valueObj with - | :? 'a as value -> value - | null -> null :> obj :?> 'a - | value -> - let value = - try value |> valueToSymbol |> symbolToValue + | :? 'a -> Some valueObj + | null -> null :> obj |> Some + | valueObj -> + let valueObj = + try valueObj |> valueToSymbol |> symbolToValue with _ -> - let value = typeof<'a>.GetDefaultValue () + let valueObj = typeof<'a>.GetDefaultValue () Log.warn "Could not gracefully promote value to the required type, so using a default value instead." - value :?> 'a + valueObj match property.PropertyValue with - | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- value + | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- valueObj | :? ComputedProperty -> () // nothing to do - | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- value - value + | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- valueObj + Some valueObj else let definitions = Reflection.getPropertyDefinitions (getType gameState.Dispatcher) - let value = + let valueObj = match List.tryFind (fun (pd : PropertyDefinition) -> pd.PropertyName = propertyName) definitions with | Some definition -> match definition.PropertyExpr with - | DefineExpr value -> value :?> 'a - | VariableExpr eval -> eval world :?> 'a - | ComputedExpr property -> property.ComputedGet game world :?> 'a + | DefineExpr valueObj -> valueObj + | VariableExpr eval -> eval world + | ComputedExpr property -> property.ComputedGet game world | None -> failwithumf () - let property = { PropertyType = typeof<'a>; PropertyValue = value } + let property = { PropertyType = typeof<'a>; PropertyValue = valueObj } gameState.Xtension <- Xtension.attachProperty propertyName property gameState.Xtension - value + Some valueObj static member internal tryGetGameXtensionValue<'a> propertyName game world : 'a voption = - // NOTE: we're only using exceptions as flow control in order to avoid code duplication and perf costs. - // TODO: P1: see if we can find a way to refactor this situation without incurring any additional overhead on the getGameXtensionValue call. - try World.getGameXtensionValue<'a> propertyName game world |> ValueSome - with _ -> ValueNone + match World.tryGetGameXtensionValueObj<'a> propertyName game world with + | Some valueObj -> valueObj :?> 'a |> ValueSome + | None -> ValueNone + + static member internal getGameXtensionValue<'a> propertyName game (world : World) = + match World.tryGetGameXtensionValueObj<'a> propertyName game world with + | Some valueObj -> valueObj :?> 'a + | None -> failwithumf () static member internal getGameProperty propertyName game world = match GameGetters.TryGetValue propertyName with @@ -701,7 +705,7 @@ module WorldModuleGame = | Some computedSet -> let previous = cp.ComputedGet (box game) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue game world |> ignore // TODO: P0: move related type definitions into Nu from Prime and modify them to match mutable usage. + computedSet property.PropertyValue game world |> ignore struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -756,7 +760,7 @@ module WorldModuleGame = previous <- cp.ComputedGet (box game) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue game world |> ignore // TODO: P0: move related type definitions into Nu from Prime and modify them to match mutable usage. + computedSet propertyOld.PropertyValue game world |> ignore | None -> () | _ -> previous <- propertyOld.PropertyValue diff --git a/Nu/Nu/World/WorldModuleGroup.fs b/Nu/Nu/World/WorldModuleGroup.fs index 9ced7a80a3..39c774c0a0 100644 --- a/Nu/Nu/World/WorldModuleGroup.fs +++ b/Nu/Nu/World/WorldModuleGroup.fs @@ -216,7 +216,7 @@ module WorldModuleGroup = | _ -> true else false - static member internal getGroupXtensionValue<'a> propertyName group world = + static member internal tryGetGroupXtensionValueObj<'a> propertyName group world = let groupState = World.getGroupState group world let mutable property = Unchecked.defaultof<_> if GroupState.tryGetProperty (propertyName, groupState, &property) then @@ -226,39 +226,43 @@ module WorldModuleGroup = | :? ComputedProperty as cp -> cp.ComputedGet group world | _ -> property.PropertyValue match valueObj with - | :? 'a as value -> value - | null -> null :> obj :?> 'a - | value -> - let value = - try value |> valueToSymbol |> symbolToValue + | :? 'a -> Some valueObj + | null -> null :> obj |> Some + | valueObj -> + let valueObj = + try valueObj |> valueToSymbol |> symbolToValue with _ -> - let value = typeof<'a>.GetDefaultValue () + let valueObj = typeof<'a>.GetDefaultValue () Log.warn "Could not gracefully promote value to the required type, so using a default value instead." - value :?> 'a + valueObj match property.PropertyValue with - | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- value + | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- valueObj | :? ComputedProperty -> () // nothing to do - | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- value - value + | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- valueObj + Some valueObj else let definitions = Reflection.getPropertyDefinitions (getType groupState.Dispatcher) - let value = + let valueObj = match List.tryFind (fun (pd : PropertyDefinition) -> pd.PropertyName = propertyName) definitions with | Some definition -> match definition.PropertyExpr with - | DefineExpr value -> value :?> 'a - | VariableExpr eval -> eval world :?> 'a - | ComputedExpr property -> property.ComputedGet group world :?> 'a + | DefineExpr valueObj -> valueObj + | VariableExpr eval -> eval world + | ComputedExpr property -> property.ComputedGet group world | None -> failwithumf () - let property = { PropertyType = typeof<'a>; PropertyValue = value } + let property = { PropertyType = typeof<'a>; PropertyValue = valueObj } groupState.Xtension <- Xtension.attachProperty propertyName property groupState.Xtension - value + Some valueObj static member internal tryGetGroupXtensionValue<'a> propertyName group world : 'a voption = - // NOTE: we're only using exceptions as flow control in order to avoid code duplication and perf costs. - // TODO: P1: see if we can find a way to refactor this situation without incurring any additional overhead on the getGroupXtensionValue call. - try World.getGroupXtensionValue<'a> propertyName group world |> ValueSome - with _ -> ValueNone + match World.tryGetGroupXtensionValueObj<'a> propertyName group world with + | Some valueObj -> valueObj :?> 'a |> ValueSome + | None -> ValueNone + + static member internal getGroupXtensionValue<'a> propertyName group (world : World) = + match World.tryGetGroupXtensionValueObj<'a> propertyName group world with + | Some valueObj -> valueObj :?> 'a + | None -> failwithumf () static member internal getGroupProperty propertyName group world = match GroupGetters.TryGetValue propertyName with @@ -285,7 +289,7 @@ module WorldModuleGroup = | Some computedSet -> let previous = cp.ComputedGet (box group) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue group world |> ignore // TODO: P0: move related type definitions into Nu from Prime and modify them to match mutable usage. + computedSet property.PropertyValue group world |> ignore struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -340,7 +344,7 @@ module WorldModuleGroup = previous <- cp.ComputedGet (box group) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue group world |> ignore // TODO: P0: move related type definitions into Nu from Prime and modify them to match mutable usage. + computedSet propertyOld.PropertyValue group world |> ignore | None -> () | _ -> previous <- propertyOld.PropertyValue diff --git a/Nu/Nu/World/WorldModuleScreen.fs b/Nu/Nu/World/WorldModuleScreen.fs index 909a499ad2..57a0b5e75b 100644 --- a/Nu/Nu/World/WorldModuleScreen.fs +++ b/Nu/Nu/World/WorldModuleScreen.fs @@ -263,7 +263,7 @@ module WorldModuleScreen = | _ -> true else false - static member internal getScreenXtensionValue<'a> propertyName screen world = + static member internal tryGetScreenXtensionValueObj<'a> propertyName screen world = let screenState = World.getScreenState screen world let mutable property = Unchecked.defaultof<_> if ScreenState.tryGetProperty (propertyName, screenState, &property) then @@ -273,39 +273,43 @@ module WorldModuleScreen = | :? ComputedProperty as cp -> cp.ComputedGet screen world | _ -> property.PropertyValue match valueObj with - | :? 'a as value -> value - | null -> null :> obj :?> 'a - | value -> - let value = - try value |> valueToSymbol |> symbolToValue + | :? 'a -> Some valueObj + | null -> null :> obj |> Some + | valueObj -> + let valueObj = + try valueObj |> valueToSymbol |> symbolToValue with _ -> - let value = typeof<'a>.GetDefaultValue () + let valueObj = typeof<'a>.GetDefaultValue () Log.warn "Could not gracefully promote value to the required type, so using a default value instead." - value :?> 'a + valueObj match property.PropertyValue with - | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- value + | :? DesignerProperty as dp -> dp.DesignerType <- typeof<'a>; dp.DesignerValue <- valueObj | :? ComputedProperty -> () // nothing to do - | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- value - value + | _ -> property.PropertyType <- typeof<'a>; property.PropertyValue <- valueObj + Some valueObj else let definitions = Reflection.getPropertyDefinitions (getType screenState.Dispatcher) - let value = + let valueObj = match List.tryFind (fun (pd : PropertyDefinition) -> pd.PropertyName = propertyName) definitions with | Some definition -> match definition.PropertyExpr with - | DefineExpr value -> value :?> 'a - | VariableExpr eval -> eval world :?> 'a - | ComputedExpr property -> property.ComputedGet screen world :?> 'a + | DefineExpr valueObj -> valueObj + | VariableExpr eval -> eval world + | ComputedExpr property -> property.ComputedGet screen world | None -> failwithumf () - let property = { PropertyType = typeof<'a>; PropertyValue = value } + let property = { PropertyType = typeof<'a>; PropertyValue = valueObj } screenState.Xtension <- Xtension.attachProperty propertyName property screenState.Xtension - value + Some valueObj static member internal tryGetScreenXtensionValue<'a> propertyName screen world : 'a voption = - // NOTE: we're only using exceptions as flow control in order to avoid code duplication and perf costs. - // TODO: P1: see if we can find a way to refactor this situation without incurring any additional overhead on the getScreenXtensionValue call. - try World.getScreenXtensionValue<'a> propertyName screen world |> ValueSome - with _ -> ValueNone + match World.tryGetScreenXtensionValueObj<'a> propertyName screen world with + | Some valueObj -> valueObj :?> 'a |> ValueSome + | None -> ValueNone + + static member internal getScreenXtensionValue<'a> propertyName screen (world : World) = + match World.tryGetScreenXtensionValueObj<'a> propertyName screen world with + | Some valueObj -> valueObj :?> 'a + | None -> failwithumf () static member internal getScreenProperty propertyName screen world = match ScreenGetters.TryGetValue propertyName with @@ -332,7 +336,7 @@ module WorldModuleScreen = | Some computedSet -> let previous = cp.ComputedGet (box screen) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue screen world |> ignore // TODO: P0: move related type definitions into Nu from Prime and modify them to match mutable usage. + computedSet property.PropertyValue screen world |> ignore struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -387,7 +391,7 @@ module WorldModuleScreen = previous <- cp.ComputedGet (box screen) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue screen world |> ignore // TODO: P0: move related type definitions into Nu from Prime and modify them to match mutable usage. + computedSet propertyOld.PropertyValue screen world |> ignore | None -> () | _ -> previous <- propertyOld.PropertyValue From e67cb356bfdab076fd7a42a693a78dc4a64ba850 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Wed, 12 Nov 2025 15:01:52 -0500 Subject: [PATCH 32/52] Implemented #1212 with preliminary icons. --- .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Nu/Nu.Gaia/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes Nu/Nu.Gaia/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes Nu/Nu.Gaia/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes Nu/Nu.Gaia/Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes Nu/Nu.Gaia/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes Nu/Nu.Gaia/Gaia.fs | 19 +++++++++++++++--- .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Nu/Nu.Pipe/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes Nu/Nu.Pipe/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes Nu/Nu.Pipe/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes Nu/Nu.Pipe/Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes Nu/Nu.Pipe/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Nu/Nu.Tests/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes Nu/Nu.Tests/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes Nu/Nu.Tests/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes Nu/Nu.Tests/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes Nu/Nu/Core/Assets.fs | 19 +++++++++++++++++- Nu/Nu/World/WorldAssets.fs | 10 ++++++++- .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Breakout ImSim/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Breakout Mmcc/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Breakout Mmcc/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Projects/Jump Box/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes Projects/Jump Box/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Jump Box/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Jump Box/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Projects/Metrics/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes Projects/Metrics/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes Projects/Metrics/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Metrics/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Projects/Nelmish/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes Projects/Nelmish/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes Projects/Nelmish/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Nelmish/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Sand Box 2d/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Sand Box 2d/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Sand Box 2d/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes .../Terra Firma/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Terra Firma/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Terra Firma/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes .../Assets/Default/AnimatedModelIcon.png | Bin 0 -> 6673 bytes Projects/Twenty 48/Assets/Default/RawIcon.png | Bin 0 -> 8265 bytes .../Twenty 48/Assets/Default/SongIcon.png | Bin 0 -> 1216 bytes .../Twenty 48/Assets/Default/SoundIcon.png | Bin 0 -> 3015 bytes .../Assets/Default/SpineSkeletonIcon.png | Bin 0 -> 9180 bytes .../Assets/Default/StaticModelIcon.png | Bin 0 -> 6442 bytes .../Twenty 48/Assets/Default/TileMapIcon.png | Bin 0 -> 6256 bytes 122 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 Nu/Nu.Gaia/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Gaia/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Gaia/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Gaia/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Gaia/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Gaia/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Gaia/Assets/Default/TileMapIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Pipe/Assets/Default/TileMapIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Template.ImSim.Empty/Assets/Default/TileMapIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Template.ImSim.Game/Assets/Default/TileMapIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Empty/Assets/Default/TileMapIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Template.Mmcc.Game/Assets/Default/TileMapIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/AnimatedModelIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/RawIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/SongIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/SoundIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/SpineSkeletonIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/StaticModelIcon.png create mode 100644 Nu/Nu.Tests/Assets/Default/TileMapIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/RawIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/SongIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/SoundIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Blaze Vector ImSim/Assets/Default/TileMapIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/RawIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/SongIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/SoundIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Blaze Vector Mmcc/Assets/Default/TileMapIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/RawIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/SongIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/SoundIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Breakout ImSim/Assets/Default/TileMapIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/RawIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/SongIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/SoundIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Breakout Mmcc/Assets/Default/TileMapIcon.png create mode 100644 Projects/Jump Box/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Jump Box/Assets/Default/RawIcon.png create mode 100644 Projects/Jump Box/Assets/Default/SongIcon.png create mode 100644 Projects/Jump Box/Assets/Default/SoundIcon.png create mode 100644 Projects/Jump Box/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Jump Box/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Jump Box/Assets/Default/TileMapIcon.png create mode 100644 Projects/Metrics/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Metrics/Assets/Default/RawIcon.png create mode 100644 Projects/Metrics/Assets/Default/SongIcon.png create mode 100644 Projects/Metrics/Assets/Default/SoundIcon.png create mode 100644 Projects/Metrics/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Metrics/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Metrics/Assets/Default/TileMapIcon.png create mode 100644 Projects/Nelmish/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Nelmish/Assets/Default/RawIcon.png create mode 100644 Projects/Nelmish/Assets/Default/SongIcon.png create mode 100644 Projects/Nelmish/Assets/Default/SoundIcon.png create mode 100644 Projects/Nelmish/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Nelmish/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Nelmish/Assets/Default/TileMapIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/RawIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/SongIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/SoundIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Sand Box 2d/Assets/Default/TileMapIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/RawIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/SongIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/SoundIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Terra Firma/Assets/Default/TileMapIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/AnimatedModelIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/RawIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/SongIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/SoundIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/SpineSkeletonIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/StaticModelIcon.png create mode 100644 Projects/Twenty 48/Assets/Default/TileMapIcon.png diff --git a/Nu/Nu.Gaia/Assets/Default/AnimatedModelIcon.png b/Nu/Nu.Gaia/Assets/Default/AnimatedModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..d90b86d6e034c34c6f81da7331a9fc0e6034e80e GIT binary patch literal 6673 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Gaia/Assets/Default/RawIcon.png b/Nu/Nu.Gaia/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Gaia/Assets/Default/SongIcon.png b/Nu/Nu.Gaia/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Gaia/Assets/Default/SoundIcon.png b/Nu/Nu.Gaia/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Gaia/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Gaia/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Gaia/Assets/Default/StaticModelIcon.png b/Nu/Nu.Gaia/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 zcmeHM`CpRR_Xitt(sIGvx0o2TOby8`w+5V~A{}#5E0;n;n@kOIY20X0!xaZB$gRdn zr&WnbBT{T65ygEnN=!|}ja<^)m(O*+|HSu)`GMDS&vVZ`_ndRjbMAQ`Xg_&+Y*R5% zfj}VJ4&mL8K_E(Cs08_T6L{2@AsfL1N;&3%g*9~5$@P!9spD>>x;!_V^|~}O=3V&^HNrqd)K}$DNpe$X02CdZU#BO zs#rI>ulwudh^tG+y>A3YO2sX@eW;*zG9B_?ejrR2V#lAQ}uNJm^Nm;{T5o1C>KY3?s7(@ZzpQr)-6|b ze2%K0m_?U(8EG;eJzDw3)g7-0%h^J^c{~vkj6hapmSTxN-i++i{g(Dw{(|m*D79dI z3C-Q)uova`Q*XCR5?--GZ5!>a&vVM83EYW(4M%+YkD{j_U7#kPqWfEF^X5LHxr2^w zNZ&&>#WgX6iS18sh~9cX4N*6SJ7u5863=cwFR@4$cn&?J`)l6+@bzy5yNhdu^u^vI z)kG2|l&rEu+CP;a_cDa@ZX5|AcRlL-Gi)0zu<;Fo{r>GpS3#PVhNM zw;#YqkY?sbEJMiE?N~x&P*>at0!OUwIEarp$*>>M2IJ1d_=saZ)x$6_?()P(9M-ZQ z{z@cc+P(1+*sd!15RuGi|CtcEpJOL~k0ok#_~Rq&D04$CSR$h%03Tt6i5;o|?>kT9 zBTzG6WlxA?pH3nn(xfX!b`MMJ>Ld{&br@d<^RPspu3zvGaG&Rcso;HABtAk#%XTmZ zOZ-0E5ovej+{tMhH21rG#tdHXp6vKrbbo|_I<5CYj_a#K_{VxJ>4FJcobJb+NMAY> zON@KTSB?V5B-)B#FH3u`#N80xJe$Ro21eHQ?Yu5Hj5&-~psIr=VOZjjz8Nw$)6m); zoR4PKb~x|#1-}Ak)VxMohA432X=b)DJVE2y8<|yX+w+5JG?%OaEb-mTLEo7)fk`73 z!L|rYDL;WDs-JD7OJbhZb`+wyLK_lcVz+Kz77hW-zDKA z7O$CYp%wN2RT^UgKQ*-n!LBd5RQWW7d}Qzpk*w@A>~EPaxW%7xK(IrKE`Vpi2&AmbR27veca14tHu|509cz)kx>#gbBU$;31csd@<4a{+H zcS5j-&YSE-RteWIvvBySYub`Q`O>U<8bPewd-W$?!#U3!{#6jI}^(<)MzGD@s?qEwB%k}aa6(=Dn z!I5&x3WFckt>jE?oAz2Oe*YYD9!Bc-$-;-mTGf4^_AG8Js!^@p-(XyFWpV$v<^PF}IN{Kc}9wMqB%le**1 z^LC5kdmExuikGyK^Bm)$-J1(raVY{X8xL#1Ot~ymU-XtQ!2B(uP1IM=Zp|ytAU#P7 z17!1D!{y=IALhE05<8p8`>YKO+}D=t|H)OI@^)&z;(R__Z;8WPzPz!wh>>=cNhS%E z9&5kI7~VzQ$X+GnH&V$ZI^8mux%>vT648C5x@1#K}XJp zeTBkJX##_xgN_sdkm8Uo%oMHW{#sh-NP;Gh#JukxAd+2D&gEm2E$d?W{}*zf2JRgN?p;tXO%o&!TnFT|QiSLFg7#jJ(#&$g^D%zbbv=G{SqQeN;~!u# z9}?#Fw|J`_e3=yj9AvCI)vdi3Bz|ROpk{(jf40TxN~YAr^Ot-~oMqkjlW0xc8`<1N z-_kg?|>Rs`Z)la4XB}qfErhds2vWqxch)9k9rAyvR zyUoK;LDX}_F^pI&ahMVebov$g%VVl9mgpB90;oi&JQ#Z6y5Q+RE0DAbBuzcBxSPt9 z{uuDVJWL);{p_d1yRsQeynXRPY2kxP2b?BeZ4#wr^F>xrt- z>oYtQGFIIMP-Ng%nCFEyZ8Wza{CDrxe@;=I_*ZBCttY^)FCcMA-=?PvBLBz(+ftwt z3wmm5wBExO+2pb@rGV8Rj11sB7Rg|U&zLBsOI_w+D%N##ahg3X7r|9N zTJGJNA4EOxnhlCAP=u5g?y7|hrjpf#76l>#BRs4bf4o4~QtJYV=3HFEdQ( zzQ2Hniz^{L!G{$`l8xX6HyS{QVRZVla);6p><8f>PdJ~2=B%Ag-VQIwl7mT#&gq$g zq>gG+fs9njJLoub7v?a*JCrVvm+}GiC(J?KMN1Qq{-_63B}}ruD8Ldao#1#bOO+JM z*S|xsPmDeTVxysUe4+ytRI6q8TL7;BCRzK9m@=4)VAIb11sEvMoF9=T` z-Tof+hkEdWLK2`TxUt6?${@6xhkdMq z6d3OCW#|KMouu+L9yVD}3l`5p54+`WX$Z}*aTl7}@GTDv+_tG>5{{Ba7LUkur8ZMvrf*sewX*2 z3;9}1=nJvAAD)Cq&UA=#3>gow??P-+!xIUp=PA$(mx5wpXj6MEfC%r!YFlZ??+&6_ z0PVt?lN>kzO2ES731ypI(gk*j&Kw^c0LT+e0&Kq;?ZOg~WR@;KmA^-`7Dw%ob628B zvGIh+Urrh`SVfT`>&D?DOqL87tc-3fk^jMs1JEC*{Vh}MzE20f+^dNh{~l?l?2;`a z;7%dfE^4e3PH3+FNH~E6zmp;QOl}tgP}%F{7GmSmevA-VUQg;ij#tEf&}Xo&N9sjJ0@@C6XSsN3d~5PMpmlWc$uWrnqJn&Twkzz>K3& zafTNF!(j^qyVMBigd@gvykUy7_rc*#t@@a8P#3DZ0EUBx2=>k*3%SOaPj50yHS1hneED0fum=E?Z0iXmG&n0fXTFMn7R6Cyz)Trt~qz-v{V3oZg?s z6zoRD*;QwY0$yi>j#8il2Vf;L%6q1GQK}2))usBipsOA=gtp47=#uZPY9i7)0{5XP zR&l8UEAn(qL08Gg-1A^sR>qVbe_9*03=_0|x`k$Gam|sjMdMMg7B!`46vf~jC|EBE zi>h6(8#aV`e)^3rc|O7Y+8~TxV~TeLXfk+`K-K7;1cxMo{2Qwqp!rgjS>5NZIIU*N za4ZIer2UKOv4r9`9U(TLPRA>Bs)Le+T@LXCxqmCDBxUk(`{3cJH6te!1!*RfO_1kT zZ}aImVJH9rbV<_0WdA1*vYaVCV+6V|U0YoOmVWXyVmRKw;fV8hN;6eWn=gmhq(_5x zLnLRGy%^+SJN6fvwb8zduLS1DZ2sZca$d@|d zEmOwgjS`@!-~G0FlYn0d@=eJajDnydn8QfRSz)LhO(;__|7UEDmwDe^7+Qr-7aTT! ztI+hac0e&0+i2R}k9$|!tnE;YysfmG-cJWwtSixZ?Mwo}3gzcflM|N=%R{s1{`kkg zWw=Z(X~08lp!YyK)Ry;g8oiQ4AmNcn-=lA9Q`UB@?MGpDXuuzpM-CVCg*xBiXNy)g zy1**CrAq)JEUTsf%bZfS(Qf|yaxvI7+bv79a+9Ab$k_dHZN9|gGNEkP8o_?nq(1f8 z>=sRsfB`JSqiSFvbAAQ#MNt|Y{BoO3YhY{?t zVjJO{GUh=_)~fv%QxybzsTd=g+q~lVXx(UerqFF6j4%N;+1C`-*OHRGI?rsO2uudN z>k})LxVbqCGrtStB@Rx0{nz@|=Wh+iOAqjT`(sMh^HeJ**S|St75|%A>!ofLhm{k> zj&JoX`uoc-j|F&CP;n*0T!!&WCC|jWX4Z9Q4EtYr{5l&gd`vAh85RYC9jLuh;YVYy9=q$@NXMqY;OQLwO0{{|m^WA3fcl IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Gaia/Assets/Default/TileMapIcon.png b/Nu/Nu.Gaia/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 Array.sortWith (fun a b -> String.Compare (a.Key, b.Key, true)) do + let packageName = packageEntry.Key let assetName = assetEntry.Key - if (assetName.ToLowerInvariant ()).Contains (AssetViewerSearchStr.ToLowerInvariant ()) then + let retained = assetName.ToLowerInvariant().Contains(AssetViewerSearchStr.ToLowerInvariant ()) + let nonIcon = not (Assets.Default.Icons.Contains (asset packageName assetName)) + if retained && nonIcon then let assetImageSize = v2Dup (ImGui.GetFontSize () + 3.0f) - match World.imGuiTryGetTextureId (asset packageEntry.Key assetName) world with + let image = + match __c assetEntry.Value with + | RawMetadata -> asset Assets.Default.PackageName "RawIcon" + | TextureMetadata _ -> asset packageName assetName + | TileMapMetadata _ -> asset Assets.Default.PackageName "TileMapIcon" + | SpineSkeletonMetadata _ -> asset Assets.Default.PackageName "SpineSkeletonIcon" + | StaticModelMetadata _ -> asset Assets.Default.PackageName "StaticModelIcon" + | AnimatedModelMetadata _ -> asset Assets.Default.PackageName "AnimatedModelIcon" + | SoundMetadata -> asset Assets.Default.PackageName "SoundIcon" + | SongMetadata -> asset Assets.Default.PackageName "SongIcon" + match World.imGuiTryGetTextureId image world with | ValueSome textureId -> ImGui.Image (nativeint textureId, assetImageSize) if ImGui.IsItemHovered ImGuiHoveredFlags.DelayShort then @@ -3410,7 +3423,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.SameLine () ImGui.TreeNodeEx (assetName, flags ||| ImGuiTreeNodeFlags.Leaf) |> ignore if ImGui.BeginDragDropSource () then // NOTE: it appears that drag-dropping only works from nodes in Dear ImGui. - let packageNameText = if Symbol.shouldBeExplicit packageEntry.Key then String.surround "\"" packageEntry.Key else packageEntry.Key + let packageNameText = if Symbol.shouldBeExplicit packageName then String.surround "\"" packageName else packageName let assetNameText = if Symbol.shouldBeExplicit assetName then String.surround "\"" assetName else assetName let assetTagStr = "[" + packageNameText + " " + assetNameText + "]" DragDropPayloadOpt <- Some assetTagStr diff --git a/Nu/Nu.Pipe/Assets/Default/AnimatedModelIcon.png b/Nu/Nu.Pipe/Assets/Default/AnimatedModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..d90b86d6e034c34c6f81da7331a9fc0e6034e80e GIT binary patch literal 6673 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Pipe/Assets/Default/RawIcon.png b/Nu/Nu.Pipe/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Pipe/Assets/Default/SongIcon.png b/Nu/Nu.Pipe/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Pipe/Assets/Default/SoundIcon.png b/Nu/Nu.Pipe/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Pipe/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Pipe/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Pipe/Assets/Default/StaticModelIcon.png b/Nu/Nu.Pipe/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 zcmeHM`CpRR_Xitt(sIGvx0o2TOby8`w+5V~A{}#5E0;n;n@kOIY20X0!xaZB$gRdn zr&WnbBT{T65ygEnN=!|}ja<^)m(O*+|HSu)`GMDS&vVZ`_ndRjbMAQ`Xg_&+Y*R5% zfj}VJ4&mL8K_E(Cs08_T6L{2@AsfL1N;&3%g*9~5$@P!9spD>>x;!_V^|~}O=3V&^HNrqd)K}$DNpe$X02CdZU#BO zs#rI>ulwudh^tG+y>A3YO2sX@eW;*zG9B_?ejrR2V#lAQ}uNJm^Nm;{T5o1C>KY3?s7(@ZzpQr)-6|b ze2%K0m_?U(8EG;eJzDw3)g7-0%h^J^c{~vkj6hapmSTxN-i++i{g(Dw{(|m*D79dI z3C-Q)uova`Q*XCR5?--GZ5!>a&vVM83EYW(4M%+YkD{j_U7#kPqWfEF^X5LHxr2^w zNZ&&>#WgX6iS18sh~9cX4N*6SJ7u5863=cwFR@4$cn&?J`)l6+@bzy5yNhdu^u^vI z)kG2|l&rEu+CP;a_cDa@ZX5|AcRlL-Gi)0zu<;Fo{r>GpS3#PVhNM zw;#YqkY?sbEJMiE?N~x&P*>at0!OUwIEarp$*>>M2IJ1d_=saZ)x$6_?()P(9M-ZQ z{z@cc+P(1+*sd!15RuGi|CtcEpJOL~k0ok#_~Rq&D04$CSR$h%03Tt6i5;o|?>kT9 zBTzG6WlxA?pH3nn(xfX!b`MMJ>Ld{&br@d<^RPspu3zvGaG&Rcso;HABtAk#%XTmZ zOZ-0E5ovej+{tMhH21rG#tdHXp6vKrbbo|_I<5CYj_a#K_{VxJ>4FJcobJb+NMAY> zON@KTSB?V5B-)B#FH3u`#N80xJe$Ro21eHQ?Yu5Hj5&-~psIr=VOZjjz8Nw$)6m); zoR4PKb~x|#1-}Ak)VxMohA432X=b)DJVE2y8<|yX+w+5JG?%OaEb-mTLEo7)fk`73 z!L|rYDL;WDs-JD7OJbhZb`+wyLK_lcVz+Kz77hW-zDKA z7O$CYp%wN2RT^UgKQ*-n!LBd5RQWW7d}Qzpk*w@A>~EPaxW%7xK(IrKE`Vpi2&AmbR27veca14tHu|509cz)kx>#gbBU$;31csd@<4a{+H zcS5j-&YSE-RteWIvvBySYub`Q`O>U<8bPewd-W$?!#U3!{#6jI}^(<)MzGD@s?qEwB%k}aa6(=Dn z!I5&x3WFckt>jE?oAz2Oe*YYD9!Bc-$-;-mTGf4^_AG8Js!^@p-(XyFWpV$v<^PF}IN{Kc}9wMqB%le**1 z^LC5kdmExuikGyK^Bm)$-J1(raVY{X8xL#1Ot~ymU-XtQ!2B(uP1IM=Zp|ytAU#P7 z17!1D!{y=IALhE05<8p8`>YKO+}D=t|H)OI@^)&z;(R__Z;8WPzPz!wh>>=cNhS%E z9&5kI7~VzQ$X+GnH&V$ZI^8mux%>vT648C5x@1#K}XJp zeTBkJX##_xgN_sdkm8Uo%oMHW{#sh-NP;Gh#JukxAd+2D&gEm2E$d?W{}*zf2JRgN?p;tXO%o&!TnFT|QiSLFg7#jJ(#&$g^D%zbbv=G{SqQeN;~!u# z9}?#Fw|J`_e3=yj9AvCI)vdi3Bz|ROpk{(jf40TxN~YAr^Ot-~oMqkjlW0xc8`<1N z-_kg?|>Rs`Z)la4XB}qfErhds2vWqxch)9k9rAyvR zyUoK;LDX}_F^pI&ahMVebov$g%VVl9mgpB90;oi&JQ#Z6y5Q+RE0DAbBuzcBxSPt9 z{uuDVJWL);{p_d1yRsQeynXRPY2kxP2b?BeZ4#wr^F>xrt- z>oYtQGFIIMP-Ng%nCFEyZ8Wza{CDrxe@;=I_*ZBCttY^)FCcMA-=?PvBLBz(+ftwt z3wmm5wBExO+2pb@rGV8Rj11sB7Rg|U&zLBsOI_w+D%N##ahg3X7r|9N zTJGJNA4EOxnhlCAP=u5g?y7|hrjpf#76l>#BRs4bf4o4~QtJYV=3HFEdQ( zzQ2Hniz^{L!G{$`l8xX6HyS{QVRZVla);6p><8f>PdJ~2=B%Ag-VQIwl7mT#&gq$g zq>gG+fs9njJLoub7v?a*JCrVvm+}GiC(J?KMN1Qq{-_63B}}ruD8Ldao#1#bOO+JM z*S|xsPmDeTVxysUe4+ytRI6q8TL7;BCRzK9m@=4)VAIb11sEvMoF9=T` z-Tof+hkEdWLK2`TxUt6?${@6xhkdMq z6d3OCW#|KMouu+L9yVD}3l`5p54+`WX$Z}*aTl7}@GTDv+_tG>5{{Ba7LUkur8ZMvrf*sewX*2 z3;9}1=nJvAAD)Cq&UA=#3>gow??P-+!xIUp=PA$(mx5wpXj6MEfC%r!YFlZ??+&6_ z0PVt?lN>kzO2ES731ypI(gk*j&Kw^c0LT+e0&Kq;?ZOg~WR@;KmA^-`7Dw%ob628B zvGIh+Urrh`SVfT`>&D?DOqL87tc-3fk^jMs1JEC*{Vh}MzE20f+^dNh{~l?l?2;`a z;7%dfE^4e3PH3+FNH~E6zmp;QOl}tgP}%F{7GmSmevA-VUQg;ij#tEf&}Xo&N9sjJ0@@C6XSsN3d~5PMpmlWc$uWrnqJn&Twkzz>K3& zafTNF!(j^qyVMBigd@gvykUy7_rc*#t@@a8P#3DZ0EUBx2=>k*3%SOaPj50yHS1hneED0fum=E?Z0iXmG&n0fXTFMn7R6Cyz)Trt~qz-v{V3oZg?s z6zoRD*;QwY0$yi>j#8il2Vf;L%6q1GQK}2))usBipsOA=gtp47=#uZPY9i7)0{5XP zR&l8UEAn(qL08Gg-1A^sR>qVbe_9*03=_0|x`k$Gam|sjMdMMg7B!`46vf~jC|EBE zi>h6(8#aV`e)^3rc|O7Y+8~TxV~TeLXfk+`K-K7;1cxMo{2Qwqp!rgjS>5NZIIU*N za4ZIer2UKOv4r9`9U(TLPRA>Bs)Le+T@LXCxqmCDBxUk(`{3cJH6te!1!*RfO_1kT zZ}aImVJH9rbV<_0WdA1*vYaVCV+6V|U0YoOmVWXyVmRKw;fV8hN;6eWn=gmhq(_5x zLnLRGy%^+SJN6fvwb8zduLS1DZ2sZca$d@|d zEmOwgjS`@!-~G0FlYn0d@=eJajDnydn8QfRSz)LhO(;__|7UEDmwDe^7+Qr-7aTT! ztI+hac0e&0+i2R}k9$|!tnE;YysfmG-cJWwtSixZ?Mwo}3gzcflM|N=%R{s1{`kkg zWw=Z(X~08lp!YyK)Ry;g8oiQ4AmNcn-=lA9Q`UB@?MGpDXuuzpM-CVCg*xBiXNy)g zy1**CrAq)JEUTsf%bZfS(Qf|yaxvI7+bv79a+9Ab$k_dHZN9|gGNEkP8o_?nq(1f8 z>=sRsfB`JSqiSFvbAAQ#MNt|Y{BoO3YhY{?t zVjJO{GUh=_)~fv%QxybzsTd=g+q~lVXx(UerqFF6j4%N;+1C`-*OHRGI?rsO2uudN z>k})LxVbqCGrtStB@Rx0{nz@|=Wh+iOAqjT`(sMh^HeJ**S|St75|%A>!ofLhm{k> zj&JoX`uoc-j|F&CP;n*0T!!&WCC|jWX4Z9Q4EtYr{5l&gd`vAh85RYC9jLuh;YVYy9=q$@NXMqY;OQLwO0{{|m^WA3fcl IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Pipe/Assets/Default/TileMapIcon.png b/Nu/Nu.Pipe/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/RawIcon.png b/Nu/Nu.Template.ImSim.Empty/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/SongIcon.png b/Nu/Nu.Template.ImSim.Empty/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/SoundIcon.png b/Nu/Nu.Template.ImSim.Empty/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Template.ImSim.Empty/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 zcma)iWn5HU*EfiOq5>i*DM(1CbV({Oq)3NImvqMk(gTQeiwHQ<($d`xN_Te&4EgLC zys!It-cRp``OP_N|M#k0Yp-=elprz>upVQfprAZ>DJ!Xhf^rKy-a@&92L76}pLT#h zR0kEA7bpdNWb5GNwyC&+I0{N}|2o@ypce|)nmzaPZg_S7Qe^hj#JdE| zwSgD@!{tDNyeqS=<)kJeef24YyU*-Cop)@s+iC6eHOs~0aE*n!ol6YwfHr0a#gplo zZT$~fJFD5r)yHzh6TMS*)e6V&vH85 z>l;x0?sdzb2hKaT1Tr{F;< zTOOnOK*Qi!4(qa?Tvfhtig^chH=o0kc=uz{@3b5}@fAv9rSZBUn2z1AdSvDB)oA zTcbHPW*wLoziz#WrCk|Goe#{T&Sxo`ZPdj#uO;awGPln8gRzq#xys@efR~!j?Oi9w zFyN}KmnmjMqd>WffyF(O^yb;Xm}GO1$N8=Bid82p?&kwLgL+;`b%k@W+3SM(x%3{p zFDL4PiITr=TZN}>X63A%aq8KG7DV&7@sk7PDna`?{H~r`N}59o zoO_oS(%-)~v3Qg+-(u=kW#{f4OhlQpdV5v4D>r=Tz^LYefu&t=^W}_XOdRFXEopQ9 ziI@HRDw(AzO*GXbT1AZvESQ3;9D3%^2!FRRd|j3?189U%>OTH1*Q;(rA(Mz5LM!%H z-BA$Nz<83(MdGw#D3^kUAv6Mw{Q6EIr?iaMO5vOVsWWlrB43bbV;FQmDRDlRe))IZ5W3Dw*8iCA^Qu7*V48|(t^jt8S}(Jkds?9+yEjS$o($r z;bUyOrT3dSu;0eV>e<}Ju}}Wcknv3(NNmKC)I~iB*EEs6L}?E%Kl|uz9B6C&%Cek9 zrEvFK=P`v!Vc%)^{VJ4u4`9EY2Xy2B3)8FTCOUqxfg?W%yeM@z^3;#oX1L+Cum$Fk5`neh}=hLX` zTeed4};CymXBwJd%uK3t4ANd%cp2%kp}}~>bgzQTFcm5tQNUINCzD{5Ug>D zT!1*WX~1Oc(9qh};j}X~GIr^Vs>~yqm1&2nj+gj_Gntig;ZJ_8E9>;bpZ)ZP#_uXm zWj5XKK4K_XIels^+P-FZVTE3 zu$IM=QRJ-7ivYm5)5I-Zi%mX3NdIDMEMW0twW!?%mcO{gz!Hg5F_cx_8c{+qlT{v3 zl36NrWyc11s9mANsM|Tl#L2XcIx<)nU4D~sVaQ)tBPNeztr*TIomq*dn93>-j5cx< za@SFepRmxzakq+}cnpac*dw-=(6z9%7Fx{!dhzi|w;MqPH&nD?3z(lv{rqXTh3G1? zv9n^!$qzfQvk1t@GUhotrJNY@OV(pBof*I$_wg*VMHdi$EPreVe@H@)HFz4_LVIr` zd&$x41YU~gp+QazEGuIl&y3PYHMf{&=TA z!aU=W(ziAEayy(GPkNBrHeE7r|@S9^Cv-ouvyiJoh5bb zjly`3eA={{Hw?NrzFtvVVcFs4%lQH25)O^@nlgbHt54#H<@0aW+_7mp;E&am zBMt9`F)zx;Ygv}VW!#z6`(ZtrW2R;hV-nM!BachuAtl#vUgKcS`SIZqLPVDA zrtlblj!MxwnvzCS2y0pSmP+;s=&8(*V9UXnS%iRrohfi7xyVu4P|F`j493yjGvtlq z+{YlFyv@#lfRMk+A2&(6@+fkUHXPFN=irY;CtFjp=o0{k***0>n7rmS&|*$3NX6k7 zIPH+QF8S$?&?Y>{lG;RxB4=7%t(=SDDzxcC)q!u*Eo?`IGShS_itF1WHjP~*0e6>w zJR}J~GVK=$xmd7dLtrZtH+-9fF3DdJ8DvOaQLiceL}xO^ZzIUXc!y8;o+`&4{W;bG zuQFtrVkq97y!-*Z>_}) z)2>pwXf>WiUDNb3O!>ufVHsiZ1aV;<9<8bsG(yD5T1WzrmPo?5KeNkuK$fYQ30gvg zG7%;-KoeN`lK^AU9Q%iHSPRe>)b*Hxruczav8Vx>6Ap}T40Eaa7@i&=Ihg%eFpxHk zJ5K2`)?f;DU<|oHYMj7&)VaFe57P-O^W)3;)M48+6yj;{eNN*B>oj(QMU2*3a?knF z;gKmmYs*{lsSNxU7sE}-8Q#Q`PE&KhAh^}9QsP_SNR8=uHJi7`G=HpIu@uwX7b%L!xJb`iSP`}8T7+C67znn#*becG18hgfSc5(A{ZB=?Og`TVZAEKnU@bSF`kx{C;hhg*wJ?ukC z9j4}rOTUfksdiK!EC?36Q7cM+5`mLVhg|qE6SsJM7x$!puDH}mHJH$`x{kAO?}q{I z2@GRDUqd)5$Z&u@J?ENoonV|j7$HbKFRi1QeJ#3w_&V|^)as@?le*$k2Bn$Yk{sXu zlj^5T7-$&JGutmSE8-4$yJML`ww*P77A=`tHi*DB{ zDhd^xh3aB2Y99{~-&Hi(5&u&_l@tNpD~v9wyt}V6Qab9P@%I)UKK46x&P(sO7NTr8 zM7=#qb@hCPHCV>%&cb=I5AJp6l|Dz552-Cti^N9Ah4)r^hrahN+Ma!gr>tm zh*vf*lm*+xZ9Mp$2jZS`dQXDWaXud<3HY7H)x5wprSyG4ea)%I8Wjm_tym(!gCC-H zw6EP+&_2Kl@jOGr?bjZinmV86Tu|2XJ58&Rz}>wuaz8#16g%ql z47k*LyEugH<#bucVuUOP8 zy;9y1S3i!23oEnq=>cFGGM1jEC?H{rs~^k4g|&F}^kDMfvG|5!KRwHEQb%D|97%wk zyWeSDjm*ch;lr2RrE~{f_w_sHp)aHj{j#a**mfU&O}{>_9VXH8uVl=uV4tTnhZx_q zM0pPN`*_UElhumrM0b*s9ME;a!WfP&h=+lSG%&9%vsj&ZjC6tRNYKcvgP6BHbJuU2*JL_Q^(h4uO9;bzOOCPu#-p@za zK1!nhwTv!0`Nyl%u(m}_peS9mC0DJy?Q7|ZchP%9;q4_ri~`*olhgaXPKnbWrJD4) zDf)*Vx9}vAwa^OOOO`P8zqq&e;+-FAe6xaFU@TuJG4Dcbb{7`Qul$OaA_4}PY zZv0xx7fV#k?_6~R+JL@fEZQq2fEXPAO376|;V?dx)cyQ1YB#B5R`=n=GAQ-}UaFBJ z^Rk*#TgBV3y;sk5kFvQeNyvfRppkfo0+B<~Vo6up|Aj`xnmVcb;TqI6pA?8{*L$?2 zG3`S7sAk%r6Y{pr*z>V!RcjJ6U29aVgCs55i7N5q)?-g0s_qMc|Q}t_f0Un;uT_sSgA% zQgOs|66b+exq?YcQx&j&wt|GopJ=2Y!wd5~po(+?x;TRAYxv+mDR26+ErPX|hnrFCyjWPO`v57h# zu=&OAgz0a=@rTkpqz~xHMEo=8Wi0Vy;5G!*U`@p2wc$I2d-^ep{4xYd)cr#crUCu1 z0(XqH&@nK;fGI7bef(wOj`X6I{B&m#*8TfnG8j%`4hYiZ^&TO|r7YoRy!m+|5nwG5 zjw7a?x{vfxE{8MQurn8Hu$*IHIHj1ktV%i@*|5psLf`ziA)iuAV0jjE&lkxFEazxL zk3>R*k}Eh34A(xvCup2Nptw%#U^Q`Byg7ow)+q`$2+0sLCtTw#G$Qr>JpchFW1GgZ zmi_zIXOIkSf&`)iGl2a$MG)%Q<uF;BB(wmrLgf1UJq|X?Y=D6RG*v`j^c3E&tz5 zVag#jlBQr_qBR(TT{swn!%73eSOZ3P12(*Z)Re{MJp$hHuM}A1<$T9B>{lsk z=}NmgJ)w2O2Q2oPvkfL*7#juD=LvTs{anz0!IP5LI)WI!nQ^s@zhn_#%)B`RmTaUx;1Jl> zM5?*}MCdL5DkBFsVj{Rae{I&!i8Pk9NDPvN&GpC)X#Zcd-6M6`C8vbM4kTFJ%-yb$ z6$zg#lED3KXaclXn3*?QCoeaNM_LQ^kEl0b)dH*1_YP;%T*i`gk?L0%21)7#L?P_* zY(gBVhL+AqcTNS<`c-0cT}KiAzg>9WhENqW%YQI-Z$_M_yQ$AjV*jow>R=|0BMJ4d zJn*l(*LqtkB6)bov41Fc+m1G{E~M)S112h*UdobtGZkC##C5`p!HO;^rQ&#Z-Q0VHHiIr=u5Y-byGgbctH3=Q>;n;|gYCe=q2uRV+pABjPIs zooxQ9ZWYxrjQM_S+1ex$Da#|VJm_Vw22HR&x(`lu&Gyzqgl}BIR>%I0+~f9s?%ZyN zc2{z-G*VpZeZa#cFRDYIz(-Cv9CQPVbRQD9ZJ;E0EW1<~TF>@MdjdbHoXHmYqc_&@S1?@Gk8DSAv&S3il*{e8R zzf;B4YmXM-sK4^8-%1+A-CYn(z21#Rj67oV1eT;7=8w}wU9VsGqyzuoFz&4^OR?z_ z2)R%+eYs3!gBVlb9o~h+>97}<0LC8hE;7bWyFh`MMbM`t|%U<30dc-m>iR*K8HA)XoO(ZwPg$({! zfgVXdMIY+xwaZ*ZaEKzgrMIebVL2s)yidE5yc$f-L>}=-{|>uaNoQa@@^_6prpmtV z7je7y9O(#8ihhM9o&@3+2SgM^svs+P2}=jqs&L zah-qu2?19;sw>mIT@g1l!W&l9IM@r9J>3jF11TNXBp20+Grs6IYIXSgyNVv-A@6GG zjbzp#7oKU9i0N=DNkP_9O%ku1kin0J&76Enz?A@NaX|LV=5 zs0hT@#G8`@@c(?|MvOg_(4n|`bB_MoLXz~zjq}q{=YJad7pp=%>6;N(UHXG}?>_yK z^nR|0gWT00M=C6NB*r1do3?!sfsYFbBWHr95Km@NwBd~y6XuQR^xk1)yp`S};lLoB6u)bA$jos ztk?KfI2*lO7DzD z1gx|g<_IU0l+quh!Cb}W5w52dU~7DJo)D{=m?;Pn3OxN8lHSxfbEsbqayg3pAZ#cl2J7Q}v0vE+CV=ag=z=?qKd{om% z^w6HMq|_82BU?rwL76WsX9l{=#Lk|>Dd!<=xCTf0{=@wN9Z1cs_cPol2oy+C<_ihh zF&qL{O6)An2`(%e5Q2QE5l66uHU$rBEieK zZ_~o`|!l*8aoS}tb+lXoMQ7PYhBE6zU!BaWR zS|&x-3F`rI8q@nMU5jqMXw}5xBaT4OT3t23T4tOAS~V}oj2svvQ*}(^Cpi0-)J%ja z#VlD%WbB><>JJ!EmhsN#FVaE{g?wW9^j=T z1H4$K8M}hkaP7YYyj%(sPe2F|{7azr5cOTXH`e#y7{h|xQ{x}c-bMfh74sE` z->|dVg?OKTu1Rqqf5l`4Ul+=gSO>ZD2Qao(ouHw|{h03=D|o)v`T3K-va~u|7AlZf zS6(Ox9C`D;NWwhUS~LZu2}5lknpcq z)6$fcJU~05lkDpb8ohLjxgb=c0OSyeo&j}?Mv^*(zOunGnOAH{nU&oj9iv64`v;A zPqpCdA|azYl--YYEkI!fK;njVEfB?)i!-7*LOL_8?sbaR-s5XE!gb{fg&l*Gv{5op zz$s{AGmfQbO<%$Sn)L2dw31s~D8&JYI4dao{YgR8c)E7qrBbxcE?&x*Y4pP>T6H%r z3c=ZFH+n(`FvSL8bwMJwgva`i|#dxnf%p+$XE znLEBum`gv*>aAqtg3TXqYEYlLBHX2DK~%9S^;`8jEQCWvTXSiqIzCdgVp&{?#ciry z(#~8cl!lXdedkfTbj$mlbEaBz*5|_eRk1AeXMF@HlGV2~oxt{H(kDrk@-q)2jao%U z&ksdbYocWSz|>XF6`03!+%5Ic=l>DE7`{OkeM$?ZzYmKli+Py`Tk_|EArTU&?bgPn^5<26a#?tyhKJ^6!12DjAkmaTo3 zYh?;ipu>St&JR#FpV4rG5DT#Fm_rmtw-o2mrOnLRDM(3FqC0^Ri*x!-Owhr>P!`~M z#?1CLzK;2N{|e%ajE8c0E3U?Ykpu4&7JV?-K&_V4S);KLSO99Z^cQ#ZjG+;9c|}mJ z_B|*l%{sQ(&KXOan>S)OI8wG5flBcZf4%PsNoFQGsr;Z5I}T~)qQy$Lgh1T|U{>DV!-DNtnGlCiSlr*js zkKGyDl(h7@SkTno`Fn?e-t5d>&&4l0I>mc^#@;WxaOE|c71rtGF%3%W5x+r+T`in-l2mc%Shr>pUzgs}X;jbsQ#sp0E1kKZ zc6IXc%c*{_&JjCptPVLg#t}RBO4i7xdv({Or+b*UAoM2u8nf<&;O) zqnaqb@*mXK#8v;G#yi2$Pjs%5pOKRMh`$PtvkD1hnQMRq>P`xm=lzLJ>0Ef@ z5-+T;v&EtT$FM2g2-~dX>z-+@E%&nPj5qF`tekFQD>(CGcBOZjg0}|fhmP8;WLsX_ z?fy2%Ij`Es+*>#%E$I=S_Ba>y4v=QH(?9X?7xfNDe3;_Zu6?ne3*vy?U5}Zi-hKy$ z$TK|sV*y+5Z^&<2+FZ`|l3%}FJAu#eHse$#a_Dbv>a*{Mu`YDroMyZsKhzdI9`L!o aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/StaticModelIcon.png b/Nu/Nu.Template.ImSim.Empty/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 zcmeHM`CpRR_Xitt(sIGvx0o2TOby8`w+5V~A{}#5E0;n;n@kOIY20X0!xaZB$gRdn zr&WnbBT{T65ygEnN=!|}ja<^)m(O*+|HSu)`GMDS&vVZ`_ndRjbMAQ`Xg_&+Y*R5% zfj}VJ4&mL8K_E(Cs08_T6L{2@AsfL1N;&3%g*9~5$@P!9spD>>x;!_V^|~}O=3V&^HNrqd)K}$DNpe$X02CdZU#BO zs#rI>ulwudh^tG+y>A3YO2sX@eW;*zG9B_?ejrR2V#lAQ}uNJm^Nm;{T5o1C>KY3?s7(@ZzpQr)-6|b ze2%K0m_?U(8EG;eJzDw3)g7-0%h^J^c{~vkj6hapmSTxN-i++i{g(Dw{(|m*D79dI z3C-Q)uova`Q*XCR5?--GZ5!>a&vVM83EYW(4M%+YkD{j_U7#kPqWfEF^X5LHxr2^w zNZ&&>#WgX6iS18sh~9cX4N*6SJ7u5863=cwFR@4$cn&?J`)l6+@bzy5yNhdu^u^vI z)kG2|l&rEu+CP;a_cDa@ZX5|AcRlL-Gi)0zu<;Fo{r>GpS3#PVhNM zw;#YqkY?sbEJMiE?N~x&P*>at0!OUwIEarp$*>>M2IJ1d_=saZ)x$6_?()P(9M-ZQ z{z@cc+P(1+*sd!15RuGi|CtcEpJOL~k0ok#_~Rq&D04$CSR$h%03Tt6i5;o|?>kT9 zBTzG6WlxA?pH3nn(xfX!b`MMJ>Ld{&br@d<^RPspu3zvGaG&Rcso;HABtAk#%XTmZ zOZ-0E5ovej+{tMhH21rG#tdHXp6vKrbbo|_I<5CYj_a#K_{VxJ>4FJcobJb+NMAY> zON@KTSB?V5B-)B#FH3u`#N80xJe$Ro21eHQ?Yu5Hj5&-~psIr=VOZjjz8Nw$)6m); zoR4PKb~x|#1-}Ak)VxMohA432X=b)DJVE2y8<|yX+w+5JG?%OaEb-mTLEo7)fk`73 z!L|rYDL;WDs-JD7OJbhZb`+wyLK_lcVz+Kz77hW-zDKA z7O$CYp%wN2RT^UgKQ*-n!LBd5RQWW7d}Qzpk*w@A>~EPaxW%7xK(IrKE`Vpi2&AmbR27veca14tHu|509cz)kx>#gbBU$;31csd@<4a{+H zcS5j-&YSE-RteWIvvBySYub`Q`O>U<8bPewd-W$?!#U3!{#6jI}^(<)MzGD@s?qEwB%k}aa6(=Dn z!I5&x3WFckt>jE?oAz2Oe*YYD9!Bc-$-;-mTGf4^_AG8Js!^@p-(XyFWpV$v<^PF}IN{Kc}9wMqB%le**1 z^LC5kdmExuikGyK^Bm)$-J1(raVY{X8xL#1Ot~ymU-XtQ!2B(uP1IM=Zp|ytAU#P7 z17!1D!{y=IALhE05<8p8`>YKO+}D=t|H)OI@^)&z;(R__Z;8WPzPz!wh>>=cNhS%E z9&5kI7~VzQ$X+GnH&V$ZI^8mux%>vT648C5x@1#K}XJp zeTBkJX##_xgN_sdkm8Uo%oMHW{#sh-NP;Gh#JukxAd+2D&gEm2E$d?W{}*zf2JRgN?p;tXO%o&!TnFT|QiSLFg7#jJ(#&$g^D%zbbv=G{SqQeN;~!u# z9}?#Fw|J`_e3=yj9AvCI)vdi3Bz|ROpk{(jf40TxN~YAr^Ot-~oMqkjlW0xc8`<1N z-_kg?|>Rs`Z)la4XB}qfErhds2vWqxch)9k9rAyvR zyUoK;LDX}_F^pI&ahMVebov$g%VVl9mgpB90;oi&JQ#Z6y5Q+RE0DAbBuzcBxSPt9 z{uuDVJWL);{p_d1yRsQeynXRPY2kxP2b?BeZ4#wr^F>xrt- z>oYtQGFIIMP-Ng%nCFEyZ8Wza{CDrxe@;=I_*ZBCttY^)FCcMA-=?PvBLBz(+ftwt z3wmm5wBExO+2pb@rGV8Rj11sB7Rg|U&zLBsOI_w+D%N##ahg3X7r|9N zTJGJNA4EOxnhlCAP=u5g?y7|hrjpf#76l>#BRs4bf4o4~QtJYV=3HFEdQ( zzQ2Hniz^{L!G{$`l8xX6HyS{QVRZVla);6p><8f>PdJ~2=B%Ag-VQIwl7mT#&gq$g zq>gG+fs9njJLoub7v?a*JCrVvm+}GiC(J?KMN1Qq{-_63B}}ruD8Ldao#1#bOO+JM z*S|xsPmDeTVxysUe4+ytRI6q8TL7;BCRzK9m@=4)VAIb11sEvMoF9=T` z-Tof+hkEdWLK2`TxUt6?${@6xhkdMq z6d3OCW#|KMouu+L9yVD}3l`5p54+`WX$Z}*aTl7}@GTDv+_tG>5{{Ba7LUkur8ZMvrf*sewX*2 z3;9}1=nJvAAD)Cq&UA=#3>gow??P-+!xIUp=PA$(mx5wpXj6MEfC%r!YFlZ??+&6_ z0PVt?lN>kzO2ES731ypI(gk*j&Kw^c0LT+e0&Kq;?ZOg~WR@;KmA^-`7Dw%ob628B zvGIh+Urrh`SVfT`>&D?DOqL87tc-3fk^jMs1JEC*{Vh}MzE20f+^dNh{~l?l?2;`a z;7%dfE^4e3PH3+FNH~E6zmp;QOl}tgP}%F{7GmSmevA-VUQg;ij#tEf&}Xo&N9sjJ0@@C6XSsN3d~5PMpmlWc$uWrnqJn&Twkzz>K3& zafTNF!(j^qyVMBigd@gvykUy7_rc*#t@@a8P#3DZ0EUBx2=>k*3%SOaPj50yHS1hneED0fum=E?Z0iXmG&n0fXTFMn7R6Cyz)Trt~qz-v{V3oZg?s z6zoRD*;QwY0$yi>j#8il2Vf;L%6q1GQK}2))usBipsOA=gtp47=#uZPY9i7)0{5XP zR&l8UEAn(qL08Gg-1A^sR>qVbe_9*03=_0|x`k$Gam|sjMdMMg7B!`46vf~jC|EBE zi>h6(8#aV`e)^3rc|O7Y+8~TxV~TeLXfk+`K-K7;1cxMo{2Qwqp!rgjS>5NZIIU*N za4ZIer2UKOv4r9`9U(TLPRA>Bs)Le+T@LXCxqmCDBxUk(`{3cJH6te!1!*RfO_1kT zZ}aImVJH9rbV<_0WdA1*vYaVCV+6V|U0YoOmVWXyVmRKw;fV8hN;6eWn=gmhq(_5x zLnLRGy%^+SJN6fvwb8zduLS1DZ2sZca$d@|d zEmOwgjS`@!-~G0FlYn0d@=eJajDnydn8QfRSz)LhO(;__|7UEDmwDe^7+Qr-7aTT! ztI+hac0e&0+i2R}k9$|!tnE;YysfmG-cJWwtSixZ?Mwo}3gzcflM|N=%R{s1{`kkg zWw=Z(X~08lp!YyK)Ry;g8oiQ4AmNcn-=lA9Q`UB@?MGpDXuuzpM-CVCg*xBiXNy)g zy1**CrAq)JEUTsf%bZfS(Qf|yaxvI7+bv79a+9Ab$k_dHZN9|gGNEkP8o_?nq(1f8 z>=sRsfB`JSqiSFvbAAQ#MNt|Y{BoO3YhY{?t zVjJO{GUh=_)~fv%QxybzsTd=g+q~lVXx(UerqFF6j4%N;+1C`-*OHRGI?rsO2uudN z>k})LxVbqCGrtStB@Rx0{nz@|=Wh+iOAqjT`(sMh^HeJ**S|St75|%A>!ofLhm{k> zj&JoX`uoc-j|F&CP;n*0T!!&WCC|jWX4Z9Q4EtYr{5l&gd`vAh85RYC9jLuh;YVYy9=q$@NXMqY;OQLwO0{{|m^WA3fcl IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/TileMapIcon.png b/Nu/Nu.Template.ImSim.Empty/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/RawIcon.png b/Nu/Nu.Template.ImSim.Game/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 zcmeHMd010d77w6oQ4&nY2si}9l8#ye8buVz1On8ub<~zpk*KV;XrRK?z~|^?H5qNB4tWzPka$D9`?NaAXwqT0jf}fy6)` z9YkKiDg**ozmr1V6BjgfWBB0Sp6>PE_06%yC*HaKX@0AXJTkR|yKTZV|F2da-C{t| zRiWhfAtnC=W(WKj;@S6|r@PcyWdlK(xkL%PQ-*kk~GNPWuQW3bSx>a3@On|AQ9P#^}MrhFk zM0JFOnW?38VRCFe;&WcMC}Tve%W$xCx=(1pjyyJ?UQsQLJ8VfWrpNDGjNkhXi7VC9 z)v~5oud#tJkG$+$xf{!ZS8uAcKFzf09eqo3l<6Z~V>8IvQvLAkU2fFO_HNtkdd$9% zkYaj9hT~W6CUqGq9h3TuSKq&zHo3+I)A3%U(rVwxvPMz^`Q&>Aa`LgXG>&-l7G`>R zwAf&L+t^RS<}J2)**S)imHw`^oz-Ys^viw_qFiGUN@krUnNW z)6>$>gy^+`i2@{K&Co>m6lZbi529>fGZRNk5$r8wtzdh(Af58FJfnUTcA5uO2LjWx@S2zshB1GzCCHmisdl9?KBmZJ3qnuj)m ziGbYs2N+4Tc54Lw0ie6kmj&}?XliI^F+D2_T@kego&spi$jah~TLvD~r#Z9zx0(u% zcAknmg|SxOR*WkKJUsGn-D=;5XJ1+9g@u3Q`_OcXBlezSDTR2N#2Bmjv!aHFu@Xlb zNuF?@5>ui$&6oF>Li4oiC#=TNmtG?EJHzVZyw(f!m=IO4;C~G(*eXL7M zxNRP$gCic32j>3^PS{Z~siQ@sd@wU^dD%K>ERY>PskSRBjPNDm!S(4BW(VKy5R;vo z3+r0i_l4QQy3nxGx%seea~Lb1n;dsG##(*YB`EmAVyEuaWp+_RF$|3*UjCh~0rZ@V zd+SPV^Wewe^S&rBFY_ga*RVeR0uYtLN}$aTc*#E;oDu33U^z39wggu5O_ksP^@<;n zRIJ*ge0QQCejW20ZUi`P!WVu*{i;!vIpf7l^7AH=y2g&MKElhKi}EAXTX1}pnff08 zdMO@U6xCCb?E2Kwt8!zs_EMzjm(a1dO`ys2hi(@nr~coK4CqPp{XMCwVQ6tXDtAy=5#X?8Ruv8wQ1z^8j$~VG@FiMoNP!)Bk^iyE*c{!2>KjPtscDj_1x|QtNshx3W zpz0M~!@~;?g~0`Usw}P`;~pjE{IU3Tmf>*u9w6(^GA6g&Z*@20Y?! zEDQ$=Z9&A7W*PUM^fo?F$S%zt_a%}(WyG6XiuJC6MYe3P@Pwy4{Sl?IF@(MM0-UiU zm&}F6nV1gaOlwGlGIsS{_Iht$;(G5SNQ6?q@(Zw3IdxyKH-khd_2AuAjiTr0XvI0y zj3#W<%!+exNM5Nft(v%9TD?-?$=NwPvZ??BpS)-$)LJD~HHj&eO<`=Uk-5+$AJbuy zZ*AwG$$%h&y*<*GxIHqd3KXFXfOp@&_`=bP9KBdaFJ2LF{{#1q7u(2h-XV=Mf61=B zpxn&w?Tff3J+n$m-R|*9hyLbr-lba!!dr*pa-in+`5Xs|kH@bl9JRTF>EMnJ7AD~3 zIqF4tfciy8I^;5!*2m6$3l7K4lZmy9{own*o$0~0#(>WK5$<$$e25eMp_?CW?h}pN zAGRs=3y4qG_RexLbMXQJkAo8^v-or^Uh#;`d@i`k<1kKF@3C|(vZEj~p9=!@3s5l_ zzx!`AZC>>&fw^Zvd{4y!`JELv7l4DO+QLV9P@8O1Vw;EGu%nf*afX!+B&4J|xB9)E z7g2ceCE=j(TtySB=fQNF7P7?MWJRlkNhLTT<_vV-DVhJ*^;WC|YPYc#`SfERQ{1Aq zqTCpbHz?ARpP_AP>zaxT53qmo`=FFZjRy#`XDly)IOzVnZ;hcC4c|Q-gC%Tyzi)(a z9O4LQQb}LYaE1CZ<5;nr?h>m&I$s%+gCNulc~l^pT9 zQjgCej9S+K#L8jlo%MIrt5N&mF8hZZ)nOM)s2!H^o=cL9)p z)f0*{_x^UZnCc1BIZ@Und}a7u2u%k@uq1kle-#?091@WzKATThv>AMW|5?uAXQ+zb z&VRUjJjteD&FJa#Jc*^~+3d{kNcE(p8OXJa&xY0s12^DFW zLzSs76Msi^Bepmt)tgAzBu%yyd~n*LA6CD~I7_`*TTYY)uw;SC;me=J&^Fn0 z^m)y+Vix5$N$!6hqIR9sF)W?>mE6^H*r60jYcx1m^`C*H6Fa2VueJA6P7lD+29z?B zMomyyOozq|6fv$3q&%QCc+FIi6t}QLv+fK)w|_8!Fu~`tKsWZVq5E-`LV=t9 z>D8a-l>%!1oZ}F3W`+jDf|>#x3K#)s)OK$28~|QOkxX(44XQuMUU!x|l2jwA<+6>= zGL}l&1znu(A5?Ym`>7vQH0+jgS#^Bfiq1_f{1dc2FvC4rH<{MeA9TV~RSGD_0KLr) V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/SongIcon.png b/Nu/Nu.Template.ImSim.Game/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/SoundIcon.png b/Nu/Nu.Template.ImSim.Game/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Template.ImSim.Game/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/StaticModelIcon.png b/Nu/Nu.Template.ImSim.Game/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/TileMapIcon.png b/Nu/Nu.Template.ImSim.Game/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/RawIcon.png b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/SongIcon.png b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/SoundIcon.png b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/StaticModelIcon.png b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/TileMapIcon.png b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 zcmeG>XH=8fwxI|pQWO!XL8Ai~M5;o95&;P!2n;$RG6MoixgaeFp-3Hy(gqX|A)?ZT zVdx5uWT*i`3j!)tq=!fiAU)xp4>5ZrAf>SQw030BS6-N2!NkFrhKMbDEcN7Sp}& zwIl*AVds zeS77{AE@5A$=zE(dE)Eb)lzy)?W1X#%y_So*ix^*bCz74oBVVZN3V72a*g4S<#MFE zoR)~ZTY52s)p&cSNcVA9aeK#R_X(pU^rVA6c8X63?O-2?o$;cxY-p0{&KsJ)$hB2( z3Ny@$=q!c|+jXPNGpJtq5kFs#z3{*q(~r}p?!p)(hs*BfN~zvOTwuG3qNqGlBzseeS7V3-Xien}>M`RK7tMx53sK z&L_X*HH4~y0V^7cG48+^*z__mPgQkz;JeZzssoFha}8?l6MTcQZlkZ^RqzqgQ*u>Bs=*F8Z}P%?T&re=Kp=CS*)wZx@8Rl2vxCJ)G7~fS=1oF zr&igbicJYcDcSsn?A@dnOM9?7)i#mZx@3x?C`vID<*)l)3M&Wnw3$M z;&MUUZEO#|9hvYpkJy~j^f8K@?BuLt@xkDformEKH`xVNsbaNs?2$>aL+ioR@H zbTS>gU!QYNT88MV+jzdTe>?|`3EhIKgKCZ@0%z7#j092CJE)rbf~FX87l*v`E;oMD zhy5g}A{9tobzX9SBx}HX;MBZiz5>#?n-uc0W)!1wH&YmY|EdU4bQNP^<^H5Fj?*o) zF{lO@JBA6t?blaE0Rs`L^U(xI+L&v2e>xETt(Q`WMSLKs&hJmxJiXP+1fr{Ao?u#5 zcKm$6<(Iu8C7P$h@xpj)w=j_{G@3xP2SULwnV{Z@zs<@HLlN`jpq;fs$SaBfA;}Zl zAYW0Gi0w{pM~35pqi&LZ6c9Qb!32&i9JK$pZcC+b3CyWY@3t6Y&cip@6wDP`tDeV$ z-+=Xm7Un-L%HbB>BPk)x?sm7G38MX;@bB!*w>GCDIQM9NYpt>kJP>ec_MM&SddM>z zq{W|fhzZ`4fb7nJIB6S#1E!$Uw*^$^f$Zl4@t4~__agr}PwO9Dm%pOW|1{=;&|eX^ zb8&a>FQrf!$}u50kP2iCoJY6f^{Y6S(X9}`%s|ZEFES7a`9{_b7|PK^=TS{UoX{b$ z94DY;<({#h2mwSIu_KDCcl||!!t#SGfH@=# zy@pVe`wZ>qjyMd4l8b9HIAv-4h&0T7LAhr`=5c)cs)@wOpwRN?2G@s9$O&9sF_B1U z?@1gb3FEK;G)D`*l&+3qne{C1yf6WRxGbo81(#!h3&zdmK%YxL(a^wuSGzl1!fXS&6Q9%~s4R znP$|XG?JEavwmNq@aEAxC49Tt4Yi0|15URD>UP&RSs^c*Jotr#$rHzw#E3g2@32g# z=C?Od5s`cCJPr!~6A9@`<|~O4b;6WUk$bnu6G|ZH2oI2U$W-76L>gdGCrowzNUf-M z+>qq96yyk!<{jQP@ z)2-%o!SQZ;kXCfhcKZ@f9oU}^>9B?FPe&wdx6e58*JBu68Nsh~Wgz&uZPQpESpy5> zpA@_`b81F+WwM3{J&^g0A^wPauZ4*$h@?4HgU4R5h9te&v)95{MgganS2cL%Rjjg? z4owYfdH^60=h%ypmhZkk4TaQ^miL)-1)HwRe9E3WG$wNKlY))u0lYfkq_$-a+-6+%Q^?*snf^aKEqKGHw3x7_Xj@@| zCcQq4;Z-cwu2r=4v{$Qd;pGWG)bn)ckg~dpn2l|Cn0Azy%{Z4p`ExNV>!d`4!BmHP zV9oA-U?>42;uYKHMD6dJYNS#x>qk;b^=^LxtmVNv<>56>ng-m5y4>n_W^ zn(6S5%=h*9kTEUszHH)^zHd;qy2^(v`E1pvirLGj?s(Z(T(mTF(Xug|y|p&;8oX%J zl`~Inw}1WuUP;s9#on3Mekm1@g<0NK;6i%=(%NQaOtTyWzxp)j>x=!_n;aLHsg;_Y zJ0qRx3<$|;=q}Q(J&akJP$QCfc^V#mp1t*r*ck1osn>IAzWLG;wo{KjTGa!@Zsy)F z^h0omqtTEb?Wb!|e&hp<$C0RBF_h#@e}s}0`7Rj4eI>i!C`Zi8gs=o%@=SLv!1; zkp@n)`Kd>yv}QWOJ3`Qfov&yZ-l+ZaitaqW>{c{D$HVIimNc)}F+!$T4*LCYrYe}P znDgq~HiwfIc_;o#GITMa3y#+Ne5=!HTuvJQsHhHF`PQY}zE8XdvV3opSW<&cLL0d+ zfFHIwf`0Q z_I*XNd7_^rFa5<71;6@@(Sdo~?yW?t>RW87z{`WeE)ljuBDCamf zDwNC?CuFZ@GFTHw3e%U>WJ_*-S12*dTIS!UTdYVEHcKi4>yXr439$jwo{Lh2 zTol5h+4{!viMpj+_?lxMZ^6Vl3c4e$*V9{%0zBK%;>p-G0s7Nv zjsmI*sC6n>bULiDtymGVI#cOq18mD1BT@PRFm$xg?L-F5Uqt@naUi8)@x}~DIS_s< z0rTp_eQgJ9Wz(LK!L*G=bns(+XxM7n*h!~Rs3XOjM|2gEx3#Fn_yaQ)$z6qUX<#K> z#{V-S6?8Ux)BkSpU|ebSe=zG*I)TADGxx}_6JEtPauT0~4usb`qFSkZQ}U8y$MD&} zMx8#YZPf_SLxT7TQHh?kDmja`oQkohE*p(HVK6ym=qCnaFx6o&#CccjmYtFQ zpP#+{xukQ|Pfu%>mrK38zA-eF&3v%kzwA-^9{7{<&QHrr4@TqilqatBY+7Gb&&)8L z?GvJ3cDc?m;yfIl-e=}lGR7aS$;+NP7589KYhvVU3_tG8!o6`6EMPA0?eeAllKi8~ zi(<30DB8n-g|~45l2hN(zg2dH#Y{OaH^{#qz4fqZIsAa*xyOsZ>up!qZLLRxv9YkF zQPh=a$8(hdZ6haR>hFy$!0f`T5dmK=I6A_XqqZ%cuxT1x>*fd>>5`dCTPDnkmHB(J z^A%a8r{Ut@ZF%xws%xH1a{w7&LYS3`P}&J{qJ6&Mk=5hHX7NL&CJbTY7E9vZJCIE#Zq5 zOyA4DA~@W-#fY8_J^eHRR89mPNiK9TiEdTyjW9a8%>5CEes*q{BF> z;q4`((!WUh$CqqISCc3>>c1pylO`!-UoaU*{gWi;k{e9IQ8mUImB{}jDJS~{2ji&f zw;R<;|5j2t-nh11Wc`z*HL4m+#8E-p9}?W8QBO(@YWr`J{8$4LDY61dPii+b?thb{ zZv#J(^=~9SsXXZ0emV~P{z=l4dIil3@cR!5W&*GxE0DlDLv#8!lGdmnv>u=rFbDr2 zsg>qJD+v7lK>}Z-MDByu{BI;V7gZ=Wz`RHy|02On7Hvj%k&i*M`@fRP@kOK39P)8! z75+`qO@2uZg;sTO4Ot{ zV(ZI$;W#A=GNyfKY@gLt85WwM7e3{o!A*LJ;2f%(!6_bY5z++k{ZY1~Uk{zeO)b3n zw%YT^lX04$zW!6XMqla(f}>33?zXN_dyQt0M{+Bv{^&L`rnf`kG{56`c`v0XeSfNa9#M4p(ES7cg@7Vaz;C>F&rH1SicnCg=Ha>5^72`=_}_ ziI`heW4k=liOR2(t|&hDAGwPNzq?R{{O!aA8VNya>^$s-F&JAi*-K5QkrXU|e%jDu zzvDS^_cIr2#(WFw@?spHI&o^#OS|7SLp%^RF?-m60QCv2ujr3e3=MPWG~aIW@VVQ$j`?2Wo;DsMBnuvDwE-BIiwOd z)o;bsz!GvP4V{OTn?oIBv46B&|HU?JFmXJ&SKP<#_C@Gh@U?O-p3-r%#y^s_xO^Hh zI&3Hru7#`4c%#iC#5>1RlsuX6p%#1}eJwOQhVajiQ|ZFIObH*C;}^N*k4o?}HlrTW zadTGiQ^S%7j;wJSv64EFIG&fON7!ZphI#yp(^pixhxGD9l>X6<6)48~e3cY`L7fa= zVT<;WKN{Hn!*^u(n_T~ZH?)j5!3ys636h&pLRk z6*80f`yP=4zLvhtAW=?5mtfgP#@xCCh8Vsm!(Vm5>BWU<1P9eEi+ybsw6zYu9)2q5 zEUrerMwe%hkq7b2;oE9{3q! zFPG}XK<$sMGc=OUdFN_mROHrtZyHgGH+uFu8MCp<7;evQp&PRh?17$7OXKW_9yY}< zkf41AiscP2oxbAyPr6m->_9Pc_NcOphPX0^wr_?vCha!%?(fh+aJCz#6MM&p6U|E3 zrbGzFzkjU_2fJG3xi}YXXRXuy0ayLIRm6oEgNzPJZs1<+Gv|?dJlPR^t%Q_A8@kX+ zmqkcBOc#b16~b?G6|tt_FLv17Pf|uPuH~z;HAn05wcG9CV73)B?m(@-_N%673aVuI zM$`BiOeRRMo0uS6TWj$%hF%`hyKlHuBO}K*_x#o@gW&89Nhg9mm^dz`DUDT{=+I== z{m};-a1t55gdIG^&+too{XQ!l7J(KX|9*>R8ZmPmt9{??7+LY{la&ZgAPw{}(UCa5 zvM7Zpr!w$(twlH%SEK$;m$%Js6B0%A}F31uIHU&C`X4G{38(K{oi&vg@OAb8?hWLcTv<0?io7S7U=@R^1*o=Iswy z1qst-Zkko$T?BXNp-N3WyKHU8B4nP8rzA}#j)Od9TNy1^*+J2 zw}~Pe$$rMU`qIb5aYRv!TxFzXXcy$9?{))MT^f0ktmt_DGzlYzGJ^+WkcnW~W0}$V zp*<8;;z8|BipWPT!;bli4EbdJUTUcmljagId`HO~tnN`!?z!R(F~ux*8ZmvRLE=6ue(o<) zg=oh50(EvS#a-I+M2Dr;=DLo$58YEcYl1cH727v<(Hb9zKfItrEL zA&pFAP44cX0H&u6i0MM1vlib?$?zX7=%5&< zc!-oGkd@aKJOrU?Vk^Nl)$)>d5t!1sXcI8JxFZToQEqxXH7$47c0ni@tNR&#rd4Xkv#L4h1g^nkot=10ixds^%v}K+H%r9X zq2f|$q{G)GOH3X1l$eV?`tWsJb&lIdT4w;*m5dwokk47$6DD0ixRQ=)?|t~oZ{Vf? zG6C<5+hnVhOsm+O1QBzzW;%CRshEgKem9Tc4AB~OW?PUOUUKU|Hns{{o|v=Ls_@oK ze*m&~&5UXB(m2m(nqC(Lk(9Vud9B;ewNlc_m`^p|l!^yik|T-gw=Z);4nq z)!uKz;^n8?bq8$=8=;W`#wLDJ%6N1E%{W`%zQgWJ>{uaJ*kxZd%&(!o7MU@b#f1+@ z5SLQW2s?6{!42u!Y!Ut>wVr~j+4L$!bBSOze0M*Z#mm`;J)KbhWm`|zCN$$fz4J*& z!uSD)jim%UO>zE^oQ0$zV*VZ zZ3M*t)I@uJv95lL{sXsTIcKOQ*<4|Vf-djunOiTSsd+S#%W-&$@6B5;Y^m8a(l!$~+t0Al z_e+;OilI>Nq`&W46YjyLR~;0KVe$C)Vw!ud4L}U_dzE9B{S&=gFCgYha-4CgmWra} zoPnr@uzd+i?-jmZz|E6nD}Zu*Sk4*fEPibcP^~O5aFsoamoY~dx@!7g8-Vz7M7I*w zYijn#jWi7*5 zi-uKAf14mx& z0bR6=;b%MtPA2UrFd79oBncHo}V zRrPl$#SQ3Ti&A?iLfhv2X-{czh_QG{FX=+Vfi)>N|5-`oirm;DxBsT3 zbee;YO611BH}hZnLrLJ!g@01OIBbzy0!QTbL;0Eg!|fw9D}Paxn^}=ZwQsgJ(MWOz zUecLE74c=7-$4NHv0HDSl8384BLwqxaGW-)?Z1W%^*|_Yq)sM89@aMMctj&b33}Kf z5VRO01-3}Xw0n{zKg)Mh{K%h~3((uiqRdzXW|f>u#!v;l9P;~>ci)K*Ol*;faRZp% zHO80e%Apz83T%*$7uBRnprgx=^ntm6AJ9lKss>%;Uj=@{oF9Itc=b+vU}B3*xdn16 ziX$)SnaR6jh0X;z{qK;{tDURgIlzXIy-12^ELpI zRu=d1!I3aWtB%Dtll$Q*X($wnkbaCh3D^L9HK=C*mM9q33dHJfgh1;-x|@aT=@KB2 zoOwumbLpOyDL1PDDEWY#ibEUCX(zOAY=n&LxQuo2WkwspOyqAyE^BWS_u1t~0>u1C zeDi4kO121;=mS6<7x8`&Xido8E90)YKb2H>cT@TfK4&hl+-0!A1qIMO#(Iv-Tb0s@ zLPIwr0P4zh+L>E`;E2lu5TO=Z4S`e-K4mVn&{ByI2wVpR!^I>I?VDXaG(md-8o6vm z756z1LBcq47%OD8$euq;$%q;!{=4o};wc_LnFRq9*-QTxgd+1$NYh7GoOb441=mMZ zaQs#=$rUBd@*GNR)_OS=BXB2!ic(Vv@`NkiKfA`4nQcQew1^$dhM%%^2~cuzY|pTc zQm}BY7J+C8Ih7;e{sdLGrgoGA;sfqRWhgfQ3Vb4-WK=GT%^@(j3vfNbaB%+W6r&mp zYYt)2&F(VWm?Z+~xdYI70=*mp6(4*oo&?=ft`z_fOhVOAC2)_n5y)@?DRnbs<&$cs zos1g@&bz2(W&@!6K8LT9Fd(A9n^zfUL1nio%0QFTRh8$u1jv*r_)MML8zP7OmB--P z<>&RX)6GrbP@!&W&wxF$vhBXp&cX@=XIs>?coIaJMSfY9rru?3_wjy^NiS-O*`P6{ zOE^{wh8k|APM#6UVWA9cm+`HK4bjropv*I6=KsLysd zig(`S_%f)1`9%PB0mQdNCbVf`HYkN6T3D=tm&B7)-4(G={p(Hf)abB%zeWmF z7OS^y?`0R7*}zX7498JJJ&Us~51rKi^k|4C7+^Xe!9k4gi*H468m-Ub-Xy(@FI$m~ zX4q0^X>|%U5aS@o8s zQYFw)D6$kj4|UyZzW9UkM^on&wwTtw&H8$fild&+SZ*I}?2mPOmcT)97Oj^}iz9*- z9UUM}AXppu*ivgcX!#G-HnK%c>*TOx+}vHcTVMvlA8i~M8&#YwSEvD{^9Ku8m&RSS zB0&8-wJGjK@yLe1FRC-MOs!c_*6aU&Rrr{o8M>%U|8VI+EG%eZ{qp@-Si*li_Rfm! ze|S!LYV~r>SBbhEOXw*elb6-f!P7PD`l2`l2*Aze4`Q=&j`?u#?p- zYG2+0-K=RQ&hgwQq}eL)FGt~W12j<5a`<=125%Y*<;UGFek2|9(tYXMybSYl<=iV_ xg6Yk?=MyX5?wuDVlTAto-7e36dAKa0xfYHZJoWoZ@HY+2)!7q!d+WiU{|Ct*j4=QJ literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/RawIcon.png b/Nu/Nu.Template.Mmcc.Game/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/SongIcon.png b/Nu/Nu.Template.Mmcc.Game/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/SoundIcon.png b/Nu/Nu.Template.Mmcc.Game/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Template.Mmcc.Game/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 zcma)iWn5HU*EfiOq5>i*DM(1CbV({Oq)3NImvqMk(gTQeiwHQ<($d`xN_Te&4EgLC zys!It-cRp``OP_N|M#k0Yp-=elprz>upVQfprAZ>DJ!Xhf^rKy-a@&92L76}pLT#h zR0kEA7bpdNWb5GNwyC&+I0{N}|2o@ypce|)nmzaPZg_S7Qe^hj#JdE| zwSgD@!{tDNyeqS=<)kJeef24YyU*-Cop)@s+iC6eHOs~0aE*n!ol6YwfHr0a#gplo zZT$~fJFD5r)yHzh6TMS*)e6V&vH85 z>l;x0?sdzb2hKaT1Tr{F;< zTOOnOK*Qi!4(qa?Tvfhtig^chH=o0kc=uz{@3b5}@fAv9rSZBUn2z1AdSvDB)oA zTcbHPW*wLoziz#WrCk|Goe#{T&Sxo`ZPdj#uO;awGPln8gRzq#xys@efR~!j?Oi9w zFyN}KmnmjMqd>WffyF(O^yb;Xm}GO1$N8=Bid82p?&kwLgL+;`b%k@W+3SM(x%3{p zFDL4PiITr=TZN}>X63A%aq8KG7DV&7@sk7PDna`?{H~r`N}59o zoO_oS(%-)~v3Qg+-(u=kW#{f4OhlQpdV5v4D>r=Tz^LYefu&t=^W}_XOdRFXEopQ9 ziI@HRDw(AzO*GXbT1AZvESQ3;9D3%^2!FRRd|j3?189U%>OTH1*Q;(rA(Mz5LM!%H z-BA$Nz<83(MdGw#D3^kUAv6Mw{Q6EIr?iaMO5vOVsWWlrB43bbV;FQmDRDlRe))IZ5W3Dw*8iCA^Qu7*V48|(t^jt8S}(Jkds?9+yEjS$o($r z;bUyOrT3dSu;0eV>e<}Ju}}Wcknv3(NNmKC)I~iB*EEs6L}?E%Kl|uz9B6C&%Cek9 zrEvFK=P`v!Vc%)^{VJ4u4`9EY2Xy2B3)8FTCOUqxfg?W%yeM@z^3;#oX1L+Cum$Fk5`neh}=hLX` zTeed4};CymXBwJd%uK3t4ANd%cp2%kp}}~>bgzQTFcm5tQNUINCzD{5Ug>D zT!1*WX~1Oc(9qh};j}X~GIr^Vs>~yqm1&2nj+gj_Gntig;ZJ_8E9>;bpZ)ZP#_uXm zWj5XKK4K_XIels^+P-FZVTE3 zu$IM=QRJ-7ivYm5)5I-Zi%mX3NdIDMEMW0twW!?%mcO{gz!Hg5F_cx_8c{+qlT{v3 zl36NrWyc11s9mANsM|Tl#L2XcIx<)nU4D~sVaQ)tBPNeztr*TIomq*dn93>-j5cx< za@SFepRmxzakq+}cnpac*dw-=(6z9%7Fx{!dhzi|w;MqPH&nD?3z(lv{rqXTh3G1? zv9n^!$qzfQvk1t@GUhotrJNY@OV(pBof*I$_wg*VMHdi$EPreVe@H@)HFz4_LVIr` zd&$x41YU~gp+QazEGuIl&y3PYHMf{&=TA z!aU=W(ziAEayy(GPkNBrHeE7r|@S9^Cv-ouvyiJoh5bb zjly`3eA={{Hw?NrzFtvVVcFs4%lQH25)O^@nlgbHt54#H<@0aW+_7mp;E&am zBMt9`F)zx;Ygv}VW!#z6`(ZtrW2R;hV-nM!BachuAtl#vUgKcS`SIZqLPVDA zrtlblj!MxwnvzCS2y0pSmP+;s=&8(*V9UXnS%iRrohfi7xyVu4P|F`j493yjGvtlq z+{YlFyv@#lfRMk+A2&(6@+fkUHXPFN=irY;CtFjp=o0{k***0>n7rmS&|*$3NX6k7 zIPH+QF8S$?&?Y>{lG;RxB4=7%t(=SDDzxcC)q!u*Eo?`IGShS_itF1WHjP~*0e6>w zJR}J~GVK=$xmd7dLtrZtH+-9fF3DdJ8DvOaQLiceL}xO^ZzIUXc!y8;o+`&4{W;bG zuQFtrVkq97y!-*Z>_}) z)2>pwXf>WiUDNb3O!>ufVHsiZ1aV;<9<8bsG(yD5T1WzrmPo?5KeNkuK$fYQ30gvg zG7%;-KoeN`lK^AU9Q%iHSPRe>)b*Hxruczav8Vx>6Ap}T40Eaa7@i&=Ihg%eFpxHk zJ5K2`)?f;DU<|oHYMj7&)VaFe57P-O^W)3;)M48+6yj;{eNN*B>oj(QMU2*3a?knF z;gKmmYs*{lsSNxU7sE}-8Q#Q`PE&KhAh^}9QsP_SNR8=uHJi7`G=HpIu@uwX7b%L!xJb`iSP`}8T7+C67znn#*becG18hgfSc5(A{ZB=?Og`TVZAEKnU@bSF`kx{C;hhg*wJ?ukC z9j4}rOTUfksdiK!EC?36Q7cM+5`mLVhg|qE6SsJM7x$!puDH}mHJH$`x{kAO?}q{I z2@GRDUqd)5$Z&u@J?ENoonV|j7$HbKFRi1QeJ#3w_&V|^)as@?le*$k2Bn$Yk{sXu zlj^5T7-$&JGutmSE8-4$yJML`ww*P77A=`tHi*DB{ zDhd^xh3aB2Y99{~-&Hi(5&u&_l@tNpD~v9wyt}V6Qab9P@%I)UKK46x&P(sO7NTr8 zM7=#qb@hCPHCV>%&cb=I5AJp6l|Dz552-Cti^N9Ah4)r^hrahN+Ma!gr>tm zh*vf*lm*+xZ9Mp$2jZS`dQXDWaXud<3HY7H)x5wprSyG4ea)%I8Wjm_tym(!gCC-H zw6EP+&_2Kl@jOGr?bjZinmV86Tu|2XJ58&Rz}>wuaz8#16g%ql z47k*LyEugH<#bucVuUOP8 zy;9y1S3i!23oEnq=>cFGGM1jEC?H{rs~^k4g|&F}^kDMfvG|5!KRwHEQb%D|97%wk zyWeSDjm*ch;lr2RrE~{f_w_sHp)aHj{j#a**mfU&O}{>_9VXH8uVl=uV4tTnhZx_q zM0pPN`*_UElhumrM0b*s9ME;a!WfP&h=+lSG%&9%vsj&ZjC6tRNYKcvgP6BHbJuU2*JL_Q^(h4uO9;bzOOCPu#-p@za zK1!nhwTv!0`Nyl%u(m}_peS9mC0DJy?Q7|ZchP%9;q4_ri~`*olhgaXPKnbWrJD4) zDf)*Vx9}vAwa^OOOO`P8zqq&e;+-FAe6xaFU@TuJG4Dcbb{7`Qul$OaA_4}PY zZv0xx7fV#k?_6~R+JL@fEZQq2fEXPAO376|;V?dx)cyQ1YB#B5R`=n=GAQ-}UaFBJ z^Rk*#TgBV3y;sk5kFvQeNyvfRppkfo0+B<~Vo6up|Aj`xnmVcb;TqI6pA?8{*L$?2 zG3`S7sAk%r6Y{pr*z>V!RcjJ6U29aVgCs55i7N5q)?-g0s_qMc|Q}t_f0Un;uT_sSgA% zQgOs|66b+exq?YcQx&j&wt|GopJ=2Y!wd5~po(+?x;TRAYxv+mDR26+ErPX|hnrFCyjWPO`v57h# zu=&OAgz0a=@rTkpqz~xHMEo=8Wi0Vy;5G!*U`@p2wc$I2d-^ep{4xYd)cr#crUCu1 z0(XqH&@nK;fGI7bef(wOj`X6I{B&m#*8TfnG8j%`4hYiZ^&TO|r7YoRy!m+|5nwG5 zjw7a?x{vfxE{8MQurn8Hu$*IHIHj1ktV%i@*|5psLf`ziA)iuAV0jjE&lkxFEazxL zk3>R*k}Eh34A(xvCup2Nptw%#U^Q`Byg7ow)+q`$2+0sLCtTw#G$Qr>JpchFW1GgZ zmi_zIXOIkSf&`)iGl2a$MG)%Q<uF;BB(wmrLgf1UJq|X?Y=D6RG*v`j^c3E&tz5 zVag#jlBQr_qBR(TT{swn!%73eSOZ3P12(*Z)Re{MJp$hHuM}A1<$T9B>{lsk z=}NmgJ)w2O2Q2oPvkfL*7#juD=LvTs{anz0!IP5LI)WI!nQ^s@zhn_#%)B`RmTaUx;1Jl> zM5?*}MCdL5DkBFsVj{Rae{I&!i8Pk9NDPvN&GpC)X#Zcd-6M6`C8vbM4kTFJ%-yb$ z6$zg#lED3KXaclXn3*?QCoeaNM_LQ^kEl0b)dH*1_YP;%T*i`gk?L0%21)7#L?P_* zY(gBVhL+AqcTNS<`c-0cT}KiAzg>9WhENqW%YQI-Z$_M_yQ$AjV*jow>R=|0BMJ4d zJn*l(*LqtkB6)bov41Fc+m1G{E~M)S112h*UdobtGZkC##C5`p!HO;^rQ&#Z-Q0VHHiIr=u5Y-byGgbctH3=Q>;n;|gYCe=q2uRV+pABjPIs zooxQ9ZWYxrjQM_S+1ex$Da#|VJm_Vw22HR&x(`lu&Gyzqgl}BIR>%I0+~f9s?%ZyN zc2{z-G*VpZeZa#cFRDYIz(-Cv9CQPVbRQD9ZJ;E0EW1<~TF>@MdjdbHoXHmYqc_&@S1?@Gk8DSAv&S3il*{e8R zzf;B4YmXM-sK4^8-%1+A-CYn(z21#Rj67oV1eT;7=8w}wU9VsGqyzuoFz&4^OR?z_ z2)R%+eYs3!gBVlb9o~h+>97}<0LC8hE;7bWyFh`MMbM`t|%U<30dc-m>iR*K8HA)XoO(ZwPg$({! zfgVXdMIY+xwaZ*ZaEKzgrMIebVL2s)yidE5yc$f-L>}=-{|>uaNoQa@@^_6prpmtV z7je7y9O(#8ihhM9o&@3+2SgM^svs+P2}=jqs&L zah-qu2?19;sw>mIT@g1l!W&l9IM@r9J>3jF11TNXBp20+Grs6IYIXSgyNVv-A@6GG zjbzp#7oKU9i0N=DNkP_9O%ku1kin0J&76Enz?A@NaX|LV=5 zs0hT@#G8`@@c(?|MvOg_(4n|`bB_MoLXz~zjq}q{=YJad7pp=%>6;N(UHXG}?>_yK z^nR|0gWT00M=C6NB*r1do3?!sfsYFbBWHr95Km@NwBd~y6XuQR^xk1)yp`S};lLoB6u)bA$jos ztk?KfI2*lO7DzD z1gx|g<_IU0l+quh!Cb}W5w52dU~7DJo)D{=m?;Pn3OxN8lHSxfbEsbqayg3pAZ#cl2J7Q}v0vE+CV=ag=z=?qKd{om% z^w6HMq|_82BU?rwL76WsX9l{=#Lk|>Dd!<=xCTf0{=@wN9Z1cs_cPol2oy+C<_ihh zF&qL{O6)An2`(%e5Q2QE5l66uHU$rBEieK zZ_~o`|!l*8aoS}tb+lXoMQ7PYhBE6zU!BaWR zS|&x-3F`rI8q@nMU5jqMXw}5xBaT4OT3t23T4tOAS~V}oj2svvQ*}(^Cpi0-)J%ja z#VlD%WbB><>JJ!EmhsN#FVaE{g?wW9^j=T z1H4$K8M}hkaP7YYyj%(sPe2F|{7azr5cOTXH`e#y7{h|xQ{x}c-bMfh74sE` z->|dVg?OKTu1Rqqf5l`4Ul+=gSO>ZD2Qao(ouHw|{h03=D|o)v`T3K-va~u|7AlZf zS6(Ox9C`D;NWwhUS~LZu2}5lknpcq z)6$fcJU~05lkDpb8ohLjxgb=c0OSyeo&j}?Mv^*(zOunGnOAH{nU&oj9iv64`v;A zPqpCdA|azYl--YYEkI!fK;njVEfB?)i!-7*LOL_8?sbaR-s5XE!gb{fg&l*Gv{5op zz$s{AGmfQbO<%$Sn)L2dw31s~D8&JYI4dao{YgR8c)E7qrBbxcE?&x*Y4pP>T6H%r z3c=ZFH+n(`FvSL8bwMJwgva`i|#dxnf%p+$XE znLEBum`gv*>aAqtg3TXqYEYlLBHX2DK~%9S^;`8jEQCWvTXSiqIzCdgVp&{?#ciry z(#~8cl!lXdedkfTbj$mlbEaBz*5|_eRk1AeXMF@HlGV2~oxt{H(kDrk@-q)2jao%U z&ksdbYocWSz|>XF6`03!+%5Ic=l>DE7`{OkeM$?ZzYmKli+Py`Tk_|EArTU&?bgPn^5<26a#?tyhKJ^6!12DjAkmaTo3 zYh?;ipu>St&JR#FpV4rG5DT#Fm_rmtw-o2mrOnLRDM(3FqC0^Ri*x!-Owhr>P!`~M z#?1CLzK;2N{|e%ajE8c0E3U?Ykpu4&7JV?-K&_V4S);KLSO99Z^cQ#ZjG+;9c|}mJ z_B|*l%{sQ(&KXOan>S)OI8wG5flBcZf4%PsNoFQGsr;Z5I}T~)qQy$Lgh1T|U{>DV!-DNtnGlCiSlr*js zkKGyDl(h7@SkTno`Fn?e-t5d>&&4l0I>mc^#@;WxaOE|c71rtGF%3%W5x+r+T`in-l2mc%Shr>pUzgs}X;jbsQ#sp0E1kKZ zc6IXc%c*{_&JjCptPVLg#t}RBO4i7xdv({Or+b*UAoM2u8nf<&;O) zqnaqb@*mXK#8v;G#yi2$Pjs%5pOKRMh`$PtvkD1hnQMRq>P`xm=lzLJ>0Ef@ z5-+T;v&EtT$FM2g2-~dX>z-+@E%&nPj5qF`tekFQD>(CGcBOZjg0}|fhmP8;WLsX_ z?fy2%Ij`Es+*>#%E$I=S_Ba>y4v=QH(?9X?7xfNDe3;_Zu6?ne3*vy?U5}Zi-hKy$ z$TK|sV*y+5Z^&<2+FZ`|l3%}FJAu#eHse$#a_Dbv>a*{Mu`YDroMyZsKhzdI9`L!o aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/StaticModelIcon.png b/Nu/Nu.Template.Mmcc.Game/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/TileMapIcon.png b/Nu/Nu.Template.Mmcc.Game/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Tests/Assets/Default/RawIcon.png b/Nu/Nu.Tests/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Nu/Nu.Tests/Assets/Default/SongIcon.png b/Nu/Nu.Tests/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Nu/Nu.Tests/Assets/Default/SoundIcon.png b/Nu/Nu.Tests/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Nu/Nu.Tests/Assets/Default/SpineSkeletonIcon.png b/Nu/Nu.Tests/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Nu/Nu.Tests/Assets/Default/StaticModelIcon.png b/Nu/Nu.Tests/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Nu/Nu.Tests/Assets/Default/TileMapIcon.png b/Nu/Nu.Tests/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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] @@ -93,4 +94,20 @@ module Assets = let [] LightProbeModelName = "LightProbeModel" let [] AnimatedModelName = "AnimatedModel" let [] SoundName = "Sound" - let [] SongName = "Song" \ No newline at end of file + let [] SongName = "Song" + let [] RawIconName = "RawIcon" + let [] TileMapIconName = "TileMapIcon" + let [] SpineSkeletonIconName = "SpineSkeletonIcon" + let [] StaticModelIconName = "StaticModelIcon" + let [] AnimatedModelIconName = "AnimatedModelIcon" + let [] SoundIconName = "SoundIcon" + let [] SongIconName = "SongIcon" + let [] IconNames = + Set.ofList + [RawIconName + TileMapIconName + SpineSkeletonIconName + StaticModelIconName + AnimatedModelIconName + SoundIconName + SongIconName] \ No newline at end of file diff --git a/Nu/Nu/World/WorldAssets.fs b/Nu/Nu/World/WorldAssets.fs index cd084f3d00..3526575419 100644 --- a/Nu/Nu/World/WorldAssets.fs +++ b/Nu/Nu/World/WorldAssets.fs @@ -78,4 +78,12 @@ module Assets = let [] LightProbeModel = asset PackageName Assets.Default.LightProbeModelName let [] AnimatedModel = asset PackageName Assets.Default.AnimatedModelName let [] Sound = asset PackageName Assets.Default.SoundName - let [] Song = asset PackageName Assets.Default.SongName \ No newline at end of file + let [] Song = asset PackageName Assets.Default.SongName + let [] RawIconIcon = asset PackageName Assets.Default.RawIconName + let [] TileMapIcon = asset PackageName Assets.Default.TileMapIconName + let [] SpineSkeletonIcon = asset PackageName Assets.Default.SpineSkeletonIconName + let [] StaticModelIcon = asset PackageName Assets.Default.StaticModelIconName + let [] AnimatedModelIcon = asset PackageName Assets.Default.AnimatedModelIconName + let [] SoundIcon = asset PackageName Assets.Default.SoundIconName + let [] SongIcon = asset PackageName Assets.Default.SongIconName + let [] Icons = Set.map (asset PackageName) Assets.Default.IconNames diff --git a/Projects/Blaze Vector ImSim/Assets/Default/AnimatedModelIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/AnimatedModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..d90b86d6e034c34c6f81da7331a9fc0e6034e80e GIT binary patch literal 6673 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 literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector ImSim/Assets/Default/RawIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector ImSim/Assets/Default/SongIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector ImSim/Assets/Default/SoundIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector ImSim/Assets/Default/SpineSkeletonIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector ImSim/Assets/Default/StaticModelIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 zcmeHM`CpRR_Xitt(sIGvx0o2TOby8`w+5V~A{}#5E0;n;n@kOIY20X0!xaZB$gRdn zr&WnbBT{T65ygEnN=!|}ja<^)m(O*+|HSu)`GMDS&vVZ`_ndRjbMAQ`Xg_&+Y*R5% zfj}VJ4&mL8K_E(Cs08_T6L{2@AsfL1N;&3%g*9~5$@P!9spD>>x;!_V^|~}O=3V&^HNrqd)K}$DNpe$X02CdZU#BO zs#rI>ulwudh^tG+y>A3YO2sX@eW;*zG9B_?ejrR2V#lAQ}uNJm^Nm;{T5o1C>KY3?s7(@ZzpQr)-6|b ze2%K0m_?U(8EG;eJzDw3)g7-0%h^J^c{~vkj6hapmSTxN-i++i{g(Dw{(|m*D79dI z3C-Q)uova`Q*XCR5?--GZ5!>a&vVM83EYW(4M%+YkD{j_U7#kPqWfEF^X5LHxr2^w zNZ&&>#WgX6iS18sh~9cX4N*6SJ7u5863=cwFR@4$cn&?J`)l6+@bzy5yNhdu^u^vI z)kG2|l&rEu+CP;a_cDa@ZX5|AcRlL-Gi)0zu<;Fo{r>GpS3#PVhNM zw;#YqkY?sbEJMiE?N~x&P*>at0!OUwIEarp$*>>M2IJ1d_=saZ)x$6_?()P(9M-ZQ z{z@cc+P(1+*sd!15RuGi|CtcEpJOL~k0ok#_~Rq&D04$CSR$h%03Tt6i5;o|?>kT9 zBTzG6WlxA?pH3nn(xfX!b`MMJ>Ld{&br@d<^RPspu3zvGaG&Rcso;HABtAk#%XTmZ zOZ-0E5ovej+{tMhH21rG#tdHXp6vKrbbo|_I<5CYj_a#K_{VxJ>4FJcobJb+NMAY> zON@KTSB?V5B-)B#FH3u`#N80xJe$Ro21eHQ?Yu5Hj5&-~psIr=VOZjjz8Nw$)6m); zoR4PKb~x|#1-}Ak)VxMohA432X=b)DJVE2y8<|yX+w+5JG?%OaEb-mTLEo7)fk`73 z!L|rYDL;WDs-JD7OJbhZb`+wyLK_lcVz+Kz77hW-zDKA z7O$CYp%wN2RT^UgKQ*-n!LBd5RQWW7d}Qzpk*w@A>~EPaxW%7xK(IrKE`Vpi2&AmbR27veca14tHu|509cz)kx>#gbBU$;31csd@<4a{+H zcS5j-&YSE-RteWIvvBySYub`Q`O>U<8bPewd-W$?!#U3!{#6jI}^(<)MzGD@s?qEwB%k}aa6(=Dn z!I5&x3WFckt>jE?oAz2Oe*YYD9!Bc-$-;-mTGf4^_AG8Js!^@p-(XyFWpV$v<^PF}IN{Kc}9wMqB%le**1 z^LC5kdmExuikGyK^Bm)$-J1(raVY{X8xL#1Ot~ymU-XtQ!2B(uP1IM=Zp|ytAU#P7 z17!1D!{y=IALhE05<8p8`>YKO+}D=t|H)OI@^)&z;(R__Z;8WPzPz!wh>>=cNhS%E z9&5kI7~VzQ$X+GnH&V$ZI^8mux%>vT648C5x@1#K}XJp zeTBkJX##_xgN_sdkm8Uo%oMHW{#sh-NP;Gh#JukxAd+2D&gEm2E$d?W{}*zf2JRgN?p;tXO%o&!TnFT|QiSLFg7#jJ(#&$g^D%zbbv=G{SqQeN;~!u# z9}?#Fw|J`_e3=yj9AvCI)vdi3Bz|ROpk{(jf40TxN~YAr^Ot-~oMqkjlW0xc8`<1N z-_kg?|>Rs`Z)la4XB}qfErhds2vWqxch)9k9rAyvR zyUoK;LDX}_F^pI&ahMVebov$g%VVl9mgpB90;oi&JQ#Z6y5Q+RE0DAbBuzcBxSPt9 z{uuDVJWL);{p_d1yRsQeynXRPY2kxP2b?BeZ4#wr^F>xrt- z>oYtQGFIIMP-Ng%nCFEyZ8Wza{CDrxe@;=I_*ZBCttY^)FCcMA-=?PvBLBz(+ftwt z3wmm5wBExO+2pb@rGV8Rj11sB7Rg|U&zLBsOI_w+D%N##ahg3X7r|9N zTJGJNA4EOxnhlCAP=u5g?y7|hrjpf#76l>#BRs4bf4o4~QtJYV=3HFEdQ( zzQ2Hniz^{L!G{$`l8xX6HyS{QVRZVla);6p><8f>PdJ~2=B%Ag-VQIwl7mT#&gq$g zq>gG+fs9njJLoub7v?a*JCrVvm+}GiC(J?KMN1Qq{-_63B}}ruD8Ldao#1#bOO+JM z*S|xsPmDeTVxysUe4+ytRI6q8TL7;BCRzK9m@=4)VAIb11sEvMoF9=T` z-Tof+hkEdWLK2`TxUt6?${@6xhkdMq z6d3OCW#|KMouu+L9yVD}3l`5p54+`WX$Z}*aTl7}@GTDv+_tG>5{{Ba7LUkur8ZMvrf*sewX*2 z3;9}1=nJvAAD)Cq&UA=#3>gow??P-+!xIUp=PA$(mx5wpXj6MEfC%r!YFlZ??+&6_ z0PVt?lN>kzO2ES731ypI(gk*j&Kw^c0LT+e0&Kq;?ZOg~WR@;KmA^-`7Dw%ob628B zvGIh+Urrh`SVfT`>&D?DOqL87tc-3fk^jMs1JEC*{Vh}MzE20f+^dNh{~l?l?2;`a z;7%dfE^4e3PH3+FNH~E6zmp;QOl}tgP}%F{7GmSmevA-VUQg;ij#tEf&}Xo&N9sjJ0@@C6XSsN3d~5PMpmlWc$uWrnqJn&Twkzz>K3& zafTNF!(j^qyVMBigd@gvykUy7_rc*#t@@a8P#3DZ0EUBx2=>k*3%SOaPj50yHS1hneED0fum=E?Z0iXmG&n0fXTFMn7R6Cyz)Trt~qz-v{V3oZg?s z6zoRD*;QwY0$yi>j#8il2Vf;L%6q1GQK}2))usBipsOA=gtp47=#uZPY9i7)0{5XP zR&l8UEAn(qL08Gg-1A^sR>qVbe_9*03=_0|x`k$Gam|sjMdMMg7B!`46vf~jC|EBE zi>h6(8#aV`e)^3rc|O7Y+8~TxV~TeLXfk+`K-K7;1cxMo{2Qwqp!rgjS>5NZIIU*N za4ZIer2UKOv4r9`9U(TLPRA>Bs)Le+T@LXCxqmCDBxUk(`{3cJH6te!1!*RfO_1kT zZ}aImVJH9rbV<_0WdA1*vYaVCV+6V|U0YoOmVWXyVmRKw;fV8hN;6eWn=gmhq(_5x zLnLRGy%^+SJN6fvwb8zduLS1DZ2sZca$d@|d zEmOwgjS`@!-~G0FlYn0d@=eJajDnydn8QfRSz)LhO(;__|7UEDmwDe^7+Qr-7aTT! ztI+hac0e&0+i2R}k9$|!tnE;YysfmG-cJWwtSixZ?Mwo}3gzcflM|N=%R{s1{`kkg zWw=Z(X~08lp!YyK)Ry;g8oiQ4AmNcn-=lA9Q`UB@?MGpDXuuzpM-CVCg*xBiXNy)g zy1**CrAq)JEUTsf%bZfS(Qf|yaxvI7+bv79a+9Ab$k_dHZN9|gGNEkP8o_?nq(1f8 z>=sRsfB`JSqiSFvbAAQ#MNt|Y{BoO3YhY{?t zVjJO{GUh=_)~fv%QxybzsTd=g+q~lVXx(UerqFF6j4%N;+1C`-*OHRGI?rsO2uudN z>k})LxVbqCGrtStB@Rx0{nz@|=Wh+iOAqjT`(sMh^HeJ**S|St75|%A>!ofLhm{k> zj&JoX`uoc-j|F&CP;n*0T!!&WCC|jWX4Z9Q4EtYr{5l&gd`vAh85RYC9jLuh;YVYy9=q$@NXMqY;OQLwO0{{|m^WA3fcl IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector ImSim/Assets/Default/TileMapIcon.png b/Projects/Blaze Vector ImSim/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/RawIcon.png b/Projects/Blaze Vector Mmcc/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/SongIcon.png b/Projects/Blaze Vector Mmcc/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/SoundIcon.png b/Projects/Blaze Vector Mmcc/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/SpineSkeletonIcon.png b/Projects/Blaze Vector Mmcc/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/StaticModelIcon.png b/Projects/Blaze Vector Mmcc/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/TileMapIcon.png b/Projects/Blaze Vector Mmcc/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Breakout ImSim/Assets/Default/RawIcon.png b/Projects/Breakout ImSim/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Breakout ImSim/Assets/Default/SongIcon.png b/Projects/Breakout ImSim/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Breakout ImSim/Assets/Default/SoundIcon.png b/Projects/Breakout ImSim/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Breakout ImSim/Assets/Default/SpineSkeletonIcon.png b/Projects/Breakout ImSim/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Breakout ImSim/Assets/Default/StaticModelIcon.png b/Projects/Breakout ImSim/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Breakout ImSim/Assets/Default/TileMapIcon.png b/Projects/Breakout ImSim/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 zcmeG>XH=8fwxI|pQWO!XL8Ai~M5;o95&;P!2n;$RG6MoixgaeFp-3Hy(gqX|A)?ZT zVdx5uWT*i`3j!)tq=!fiAU)xp4>5ZrAf>SQw030BS6-N2!NkFrhKMbDEcN7Sp}& zwIl*AVds zeS77{AE@5A$=zE(dE)Eb)lzy)?W1X#%y_So*ix^*bCz74oBVVZN3V72a*g4S<#MFE zoR)~ZTY52s)p&cSNcVA9aeK#R_X(pU^rVA6c8X63?O-2?o$;cxY-p0{&KsJ)$hB2( z3Ny@$=q!c|+jXPNGpJtq5kFs#z3{*q(~r}p?!p)(hs*BfN~zvOTwuG3qNqGlBzseeS7V3-Xien}>M`RK7tMx53sK z&L_X*HH4~y0V^7cG48+^*z__mPgQkz;JeZzssoFha}8?l6MTcQZlkZ^RqzqgQ*u>Bs=*F8Z}P%?T&re=Kp=CS*)wZx@8Rl2vxCJ)G7~fS=1oF zr&igbicJYcDcSsn?A@dnOM9?7)i#mZx@3x?C`vID<*)l)3M&Wnw3$M z;&MUUZEO#|9hvYpkJy~j^f8K@?BuLt@xkDformEKH`xVNsbaNs?2$>aL+ioR@H zbTS>gU!QYNT88MV+jzdTe>?|`3EhIKgKCZ@0%z7#j092CJE)rbf~FX87l*v`E;oMD zhy5g}A{9tobzX9SBx}HX;MBZiz5>#?n-uc0W)!1wH&YmY|EdU4bQNP^<^H5Fj?*o) zF{lO@JBA6t?blaE0Rs`L^U(xI+L&v2e>xETt(Q`WMSLKs&hJmxJiXP+1fr{Ao?u#5 zcKm$6<(Iu8C7P$h@xpj)w=j_{G@3xP2SULwnV{Z@zs<@HLlN`jpq;fs$SaBfA;}Zl zAYW0Gi0w{pM~35pqi&LZ6c9Qb!32&i9JK$pZcC+b3CyWY@3t6Y&cip@6wDP`tDeV$ z-+=Xm7Un-L%HbB>BPk)x?sm7G38MX;@bB!*w>GCDIQM9NYpt>kJP>ec_MM&SddM>z zq{W|fhzZ`4fb7nJIB6S#1E!$Uw*^$^f$Zl4@t4~__agr}PwO9Dm%pOW|1{=;&|eX^ zb8&a>FQrf!$}u50kP2iCoJY6f^{Y6S(X9}`%s|ZEFES7a`9{_b7|PK^=TS{UoX{b$ z94DY;<({#h2mwSIu_KDCcl||!!t#SGfH@=# zy@pVe`wZ>qjyMd4l8b9HIAv-4h&0T7LAhr`=5c)cs)@wOpwRN?2G@s9$O&9sF_B1U z?@1gb3FEK;G)D`*l&+3qne{C1yf6WRxGbo81(#!h3&zdmK%YxL(a^wuSGzl1!fXS&6Q9%~s4R znP$|XG?JEavwmNq@aEAxC49Tt4Yi0|15URD>UP&RSs^c*Jotr#$rHzw#E3g2@32g# z=C?Od5s`cCJPr!~6A9@`<|~O4b;6WUk$bnu6G|ZH2oI2U$W-76L>gdGCrowzNUf-M z+>qq96yyk!<{jQP@ z)2-%o!SQZ;kXCfhcKZ@f9oU}^>9B?FPe&wdx6e58*JBu68Nsh~Wgz&uZPQpESpy5> zpA@_`b81F+WwM3{J&^g0A^wPauZ4*$h@?4HgU4R5h9te&v)95{MgganS2cL%Rjjg? z4owYfdH^60=h%ypmhZkk4TaQ^miL)-1)HwRe9E3WG$wNKlY))u0lYfkq_$-a+-6+%Q^?*snf^aKEqKGHw3x7_Xj@@| zCcQq4;Z-cwu2r=4v{$Qd;pGWG)bn)ckg~dpn2l|Cn0Azy%{Z4p`ExNV>!d`4!BmHP zV9oA-U?>42;uYKHMD6dJYNS#x>qk;b^=^LxtmVNv<>56>ng-m5y4>n_W^ zn(6S5%=h*9kTEUszHH)^zHd;qy2^(v`E1pvirLGj?s(Z(T(mTF(Xug|y|p&;8oX%J zl`~Inw}1WuUP;s9#on3Mekm1@g<0NK;6i%=(%NQaOtTyWzxp)j>x=!_n;aLHsg;_Y zJ0qRx3<$|;=q}Q(J&akJP$QCfc^V#mp1t*r*ck1osn>IAzWLG;wo{KjTGa!@Zsy)F z^h0omqtTEb?Wb!|e&hp<$C0RBF_h#@e}s}0`7Rj4eI>i!C`Zi8gs=o%@=SLv!1; zkp@n)`Kd>yv}QWOJ3`Qfov&yZ-l+ZaitaqW>{c{D$HVIimNc)}F+!$T4*LCYrYe}P znDgq~HiwfIc_;o#GITMa3y#+Ne5=!HTuvJQsHhHF`PQY}zE8XdvV3opSW<&cLL0d+ zfFHIwf`0Q z_I*XNd7_^rFa5<71;6@@(Sdo~?yW?t>RW87z{`WeE)ljuBDCamf zDwNC?CuFZ@GFTHw3e%U>WJ_*-S12*dTIS!UTdYVEHcKi4>yXr439$jwo{Lh2 zTol5h+4{!viMpj+_?lxMZ^6Vl3c4e$*V9{%0zBK%;>p-G0s7Nv zjsmI*sC6n>bULiDtymGVI#cOq18mD1BT@PRFm$xg?L-F5Uqt@naUi8)@x}~DIS_s< z0rTp_eQgJ9Wz(LK!L*G=bns(+XxM7n*h!~Rs3XOjM|2gEx3#Fn_yaQ)$z6qUX<#K> z#{V-S6?8Ux)BkSpU|ebSe=zG*I)TADGxx}_6JEtPauT0~4usb`qFSkZQ}U8y$MD&} zMx8#YZPf_SLxT7TQHh?kDmja`oQkohE*p(HVK6ym=qCnaFx6o&#CccjmYtFQ zpP#+{xukQ|Pfu%>mrK38zA-eF&3v%kzwA-^9{7{<&QHrr4@TqilqatBY+7Gb&&)8L z?GvJ3cDc?m;yfIl-e=}lGR7aS$;+NP7589KYhvVU3_tG8!o6`6EMPA0?eeAllKi8~ zi(<30DB8n-g|~45l2hN(zg2dH#Y{OaH^{#qz4fqZIsAa*xyOsZ>up!qZLLRxv9YkF zQPh=a$8(hdZ6haR>hFy$!0f`T5dmK=I6A_XqqZ%cuxT1x>*fd>>5`dCTPDnkmHB(J z^A%a8r{Ut@ZF%xws%xH1a{w7&LYS3`P}&J{qJ6&Mk=5hHX7NL&CJbTY7E9vZJCIE#Zq5 zOyA4DA~@W-#fY8_J^eHRR89mPNiK9TiEdTyjW9a8%>5CEes*q{BF> z;q4`((!WUh$CqqISCc3>>c1pylO`!-UoaU*{gWi;k{e9IQ8mUImB{}jDJS~{2ji&f zw;R<;|5j2t-nh11Wc`z*HL4m+#8E-p9}?W8QBO(@YWr`J{8$4LDY61dPii+b?thb{ zZv#J(^=~9SsXXZ0emV~P{z=l4dIil3@cR!5W&*GxE0DlDLv#8!lGdmnv>u=rFbDr2 zsg>qJD+v7lK>}Z-MDByu{BI;V7gZ=Wz`RHy|02On7Hvj%k&i*M`@fRP@kOK39P)8! z75+`qO@2uZg;sTO4Ot{ zV(ZI$;W#A=GNyfKY@gLt85WwM7e3{o!A*LJ;2f%(!6_bY5z++k{ZY1~Uk{zeO)b3n zw%YT^lX04$zW!6XMqla(f}>33?zXN_dyQt0M{+Bv{^&L`rnf`kG{56`c`v0XeSfNa9#M4p(ES7cg@7Vaz;C>F&rH1SicnCg=Ha>5^72`=_}_ ziI`heW4k=liOR2(t|&hDAGwPNzq?R{{O!aA8VNya>^$s-F&JAi*-K5QkrXU|e%jDu zzvDS^_cIr2#(WFw@?spHI&o^#OS|7SLp%^RF?-m60QCv2ujr3e3=MPWG~aIW@VVQ$j`?2Wo;DsMBnuvDwE-BIiwOd z)o;bsz!GvP4V{OTn?oIBv46B&|HU?JFmXJ&SKP<#_C@Gh@U?O-p3-r%#y^s_xO^Hh zI&3Hru7#`4c%#iC#5>1RlsuX6p%#1}eJwOQhVajiQ|ZFIObH*C;}^N*k4o?}HlrTW zadTGiQ^S%7j;wJSv64EFIG&fON7!ZphI#yp(^pixhxGD9l>X6<6)48~e3cY`L7fa= zVT<;WKN{Hn!*^u(n_T~ZH?)j5!3ys636h&pLRk z6*80f`yP=4zLvhtAW=?5mtfgP#@xCCh8Vsm!(Vm5>BWU<1P9eEi+ybsw6zYu9)2q5 zEUrerMwe%hkq7b2;oE9{3q! zFPG}XK<$sMGc=OUdFN_mROHrtZyHgGH+uFu8MCp<7;evQp&PRh?17$7OXKW_9yY}< zkf41AiscP2oxbAyPr6m->_9Pc_NcOphPX0^wr_?vCha!%?(fh+aJCz#6MM&p6U|E3 zrbGzFzkjU_2fJG3xi}YXXRXuy0ayLIRm6oEgNzPJZs1<+Gv|?dJlPR^t%Q_A8@kX+ zmqkcBOc#b16~b?G6|tt_FLv17Pf|uPuH~z;HAn05wcG9CV73)B?m(@-_N%673aVuI zM$`BiOeRRMo0uS6TWj$%hF%`hyKlHuBO}K*_x#o@gW&89Nhg9mm^dz`DUDT{=+I== z{m};-a1t55gdIG^&+too{XQ!l7J(KX|9*>R8ZmPmt9{??7+LY{la&ZgAPw{}(UCa5 zvM7Zpr!w$(twlH%SEK$;m$%Js6B0%A}F31uIHU&C`X4G{38(K{oi&vg@OAb8?hWLcTv<0?io7S7U=@R^1*o=Iswy z1qst-Zkko$T?BXNp-N3WyKHU8B4nP8rzA}#j)Od9TNy1^*+J2 zw}~Pe$$rMU`qIb5aYRv!TxFzXXcy$9?{))MT^f0ktmt_DGzlYzGJ^+WkcnW~W0}$V zp*<8;;z8|BipWPT!;bli4EbdJUTUcmljagId`HO~tnN`!?z!R(F~ux*8ZmvRLE=6ue(o<) zg=oh50(EvS#a-I+M2Dr;=DLo$58YEcYl1cH727v<(Hb9zKfItrEL zA&pFAP44cX0H&u6i0MM1vlib?$?zX7=%5&< zc!-oGkd@aKJOrU?Vk^Nl)$)>d5t!1sXcI8JxFZToQEqxXH7$47c0ni@tNR&#rd4Xkv#L4h1g^nkot=10ixds^%v}K+H%r9X zq2f|$q{G)GOH3X1l$eV?`tWsJb&lIdT4w;*m5dwokk47$6DD0ixRQ=)?|t~oZ{Vf? zG6C<5+hnVhOsm+O1QBzzW;%CRshEgKem9Tc4AB~OW?PUOUUKU|Hns{{o|v=Ls_@oK ze*m&~&5UXB(m2m(nqC(Lk(9Vud9B;ewNlc_m`^p|l!^yik|T-gw=Z);4nq z)!uKz;^n8?bq8$=8=;W`#wLDJ%6N1E%{W`%zQgWJ>{uaJ*kxZd%&(!o7MU@b#f1+@ z5SLQW2s?6{!42u!Y!Ut>wVr~j+4L$!bBSOze0M*Z#mm`;J)KbhWm`|zCN$$fz4J*& z!uSD)jim%UO>zE^oQ0$zV*VZ zZ3M*t)I@uJv95lL{sXsTIcKOQ*<4|Vf-djunOiTSsd+S#%W-&$@6B5;Y^m8a(l!$~+t0Al z_e+;OilI>Nq`&W46YjyLR~;0KVe$C)Vw!ud4L}U_dzE9B{S&=gFCgYha-4CgmWra} zoPnr@uzd+i?-jmZz|E6nD}Zu*Sk4*fEPibcP^~O5aFsoamoY~dx@!7g8-Vz7M7I*w zYijn#jWi7*5 zi-uKAf14mx& z0bR6=;b%MtPA2UrFd79oBncHo}V zRrPl$#SQ3Ti&A?iLfhv2X-{czh_QG{FX=+Vfi)>N|5-`oirm;DxBsT3 zbee;YO611BH}hZnLrLJ!g@01OIBbzy0!QTbL;0Eg!|fw9D}Paxn^}=ZwQsgJ(MWOz zUecLE74c=7-$4NHv0HDSl8384BLwqxaGW-)?Z1W%^*|_Yq)sM89@aMMctj&b33}Kf z5VRO01-3}Xw0n{zKg)Mh{K%h~3((uiqRdzXW|f>u#!v;l9P;~>ci)K*Ol*;faRZp% zHO80e%Apz83T%*$7uBRnprgx=^ntm6AJ9lKss>%;Uj=@{oF9Itc=b+vU}B3*xdn16 ziX$)SnaR6jh0X;z{qK;{tDURgIlzXIy-12^ELpI zRu=d1!I3aWtB%Dtll$Q*X($wnkbaCh3D^L9HK=C*mM9q33dHJfgh1;-x|@aT=@KB2 zoOwumbLpOyDL1PDDEWY#ibEUCX(zOAY=n&LxQuo2WkwspOyqAyE^BWS_u1t~0>u1C zeDi4kO121;=mS6<7x8`&Xido8E90)YKb2H>cT@TfK4&hl+-0!A1qIMO#(Iv-Tb0s@ zLPIwr0P4zh+L>E`;E2lu5TO=Z4S`e-K4mVn&{ByI2wVpR!^I>I?VDXaG(md-8o6vm z756z1LBcq47%OD8$euq;$%q;!{=4o};wc_LnFRq9*-QTxgd+1$NYh7GoOb441=mMZ zaQs#=$rUBd@*GNR)_OS=BXB2!ic(Vv@`NkiKfA`4nQcQew1^$dhM%%^2~cuzY|pTc zQm}BY7J+C8Ih7;e{sdLGrgoGA;sfqRWhgfQ3Vb4-WK=GT%^@(j3vfNbaB%+W6r&mp zYYt)2&F(VWm?Z+~xdYI70=*mp6(4*oo&?=ft`z_fOhVOAC2)_n5y)@?DRnbs<&$cs zos1g@&bz2(W&@!6K8LT9Fd(A9n^zfUL1nio%0QFTRh8$u1jv*r_)MML8zP7OmB--P z<>&RX)6GrbP@!&W&wxF$vhBXp&cX@=XIs>?coIaJMSfY9rru?3_wjy^NiS-O*`P6{ zOE^{wh8k|APM#6UVWA9cm+`HK4bjropv*I6=KsLysd zig(`S_%f)1`9%PB0mQdNCbVf`HYkN6T3D=tm&B7)-4(G={p(Hf)abB%zeWmF z7OS^y?`0R7*}zX7498JJJ&Us~51rKi^k|4C7+^Xe!9k4gi*H468m-Ub-Xy(@FI$m~ zX4q0^X>|%U5aS@o8s zQYFw)D6$kj4|UyZzW9UkM^on&wwTtw&H8$fild&+SZ*I}?2mPOmcT)97Oj^}iz9*- z9UUM}AXppu*ivgcX!#G-HnK%c>*TOx+}vHcTVMvlA8i~M8&#YwSEvD{^9Ku8m&RSS zB0&8-wJGjK@yLe1FRC-MOs!c_*6aU&Rrr{o8M>%U|8VI+EG%eZ{qp@-Si*li_Rfm! ze|S!LYV~r>SBbhEOXw*elb6-f!P7PD`l2`l2*Aze4`Q=&j`?u#?p- zYG2+0-K=RQ&hgwQq}eL)FGt~W12j<5a`<=125%Y*<;UGFek2|9(tYXMybSYl<=iV_ xg6Yk?=MyX5?wuDVlTAto-7e36dAKa0xfYHZJoWoZ@HY+2)!7q!d+WiU{|Ct*j4=QJ literal 0 HcmV?d00001 diff --git a/Projects/Breakout Mmcc/Assets/Default/RawIcon.png b/Projects/Breakout Mmcc/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Breakout Mmcc/Assets/Default/SongIcon.png b/Projects/Breakout Mmcc/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Breakout Mmcc/Assets/Default/SoundIcon.png b/Projects/Breakout Mmcc/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Breakout Mmcc/Assets/Default/SpineSkeletonIcon.png b/Projects/Breakout Mmcc/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Breakout Mmcc/Assets/Default/StaticModelIcon.png b/Projects/Breakout Mmcc/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Breakout Mmcc/Assets/Default/TileMapIcon.png b/Projects/Breakout Mmcc/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Jump Box/Assets/Default/RawIcon.png b/Projects/Jump Box/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Jump Box/Assets/Default/SongIcon.png b/Projects/Jump Box/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Jump Box/Assets/Default/SoundIcon.png b/Projects/Jump Box/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Jump Box/Assets/Default/SpineSkeletonIcon.png b/Projects/Jump Box/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Jump Box/Assets/Default/StaticModelIcon.png b/Projects/Jump Box/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Jump Box/Assets/Default/TileMapIcon.png b/Projects/Jump Box/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Metrics/Assets/Default/RawIcon.png b/Projects/Metrics/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Metrics/Assets/Default/SongIcon.png b/Projects/Metrics/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Metrics/Assets/Default/SoundIcon.png b/Projects/Metrics/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Metrics/Assets/Default/SpineSkeletonIcon.png b/Projects/Metrics/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Metrics/Assets/Default/StaticModelIcon.png b/Projects/Metrics/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Metrics/Assets/Default/TileMapIcon.png b/Projects/Metrics/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Nelmish/Assets/Default/RawIcon.png b/Projects/Nelmish/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Nelmish/Assets/Default/SongIcon.png b/Projects/Nelmish/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Nelmish/Assets/Default/SoundIcon.png b/Projects/Nelmish/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Nelmish/Assets/Default/SpineSkeletonIcon.png b/Projects/Nelmish/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Nelmish/Assets/Default/StaticModelIcon.png b/Projects/Nelmish/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 zcmeHM`CpRR_Xitt(sIGvx0o2TOby8`w+5V~A{}#5E0;n;n@kOIY20X0!xaZB$gRdn zr&WnbBT{T65ygEnN=!|}ja<^)m(O*+|HSu)`GMDS&vVZ`_ndRjbMAQ`Xg_&+Y*R5% zfj}VJ4&mL8K_E(Cs08_T6L{2@AsfL1N;&3%g*9~5$@P!9spD>>x;!_V^|~}O=3V&^HNrqd)K}$DNpe$X02CdZU#BO zs#rI>ulwudh^tG+y>A3YO2sX@eW;*zG9B_?ejrR2V#lAQ}uNJm^Nm;{T5o1C>KY3?s7(@ZzpQr)-6|b ze2%K0m_?U(8EG;eJzDw3)g7-0%h^J^c{~vkj6hapmSTxN-i++i{g(Dw{(|m*D79dI z3C-Q)uova`Q*XCR5?--GZ5!>a&vVM83EYW(4M%+YkD{j_U7#kPqWfEF^X5LHxr2^w zNZ&&>#WgX6iS18sh~9cX4N*6SJ7u5863=cwFR@4$cn&?J`)l6+@bzy5yNhdu^u^vI z)kG2|l&rEu+CP;a_cDa@ZX5|AcRlL-Gi)0zu<;Fo{r>GpS3#PVhNM zw;#YqkY?sbEJMiE?N~x&P*>at0!OUwIEarp$*>>M2IJ1d_=saZ)x$6_?()P(9M-ZQ z{z@cc+P(1+*sd!15RuGi|CtcEpJOL~k0ok#_~Rq&D04$CSR$h%03Tt6i5;o|?>kT9 zBTzG6WlxA?pH3nn(xfX!b`MMJ>Ld{&br@d<^RPspu3zvGaG&Rcso;HABtAk#%XTmZ zOZ-0E5ovej+{tMhH21rG#tdHXp6vKrbbo|_I<5CYj_a#K_{VxJ>4FJcobJb+NMAY> zON@KTSB?V5B-)B#FH3u`#N80xJe$Ro21eHQ?Yu5Hj5&-~psIr=VOZjjz8Nw$)6m); zoR4PKb~x|#1-}Ak)VxMohA432X=b)DJVE2y8<|yX+w+5JG?%OaEb-mTLEo7)fk`73 z!L|rYDL;WDs-JD7OJbhZb`+wyLK_lcVz+Kz77hW-zDKA z7O$CYp%wN2RT^UgKQ*-n!LBd5RQWW7d}Qzpk*w@A>~EPaxW%7xK(IrKE`Vpi2&AmbR27veca14tHu|509cz)kx>#gbBU$;31csd@<4a{+H zcS5j-&YSE-RteWIvvBySYub`Q`O>U<8bPewd-W$?!#U3!{#6jI}^(<)MzGD@s?qEwB%k}aa6(=Dn z!I5&x3WFckt>jE?oAz2Oe*YYD9!Bc-$-;-mTGf4^_AG8Js!^@p-(XyFWpV$v<^PF}IN{Kc}9wMqB%le**1 z^LC5kdmExuikGyK^Bm)$-J1(raVY{X8xL#1Ot~ymU-XtQ!2B(uP1IM=Zp|ytAU#P7 z17!1D!{y=IALhE05<8p8`>YKO+}D=t|H)OI@^)&z;(R__Z;8WPzPz!wh>>=cNhS%E z9&5kI7~VzQ$X+GnH&V$ZI^8mux%>vT648C5x@1#K}XJp zeTBkJX##_xgN_sdkm8Uo%oMHW{#sh-NP;Gh#JukxAd+2D&gEm2E$d?W{}*zf2JRgN?p;tXO%o&!TnFT|QiSLFg7#jJ(#&$g^D%zbbv=G{SqQeN;~!u# z9}?#Fw|J`_e3=yj9AvCI)vdi3Bz|ROpk{(jf40TxN~YAr^Ot-~oMqkjlW0xc8`<1N z-_kg?|>Rs`Z)la4XB}qfErhds2vWqxch)9k9rAyvR zyUoK;LDX}_F^pI&ahMVebov$g%VVl9mgpB90;oi&JQ#Z6y5Q+RE0DAbBuzcBxSPt9 z{uuDVJWL);{p_d1yRsQeynXRPY2kxP2b?BeZ4#wr^F>xrt- z>oYtQGFIIMP-Ng%nCFEyZ8Wza{CDrxe@;=I_*ZBCttY^)FCcMA-=?PvBLBz(+ftwt z3wmm5wBExO+2pb@rGV8Rj11sB7Rg|U&zLBsOI_w+D%N##ahg3X7r|9N zTJGJNA4EOxnhlCAP=u5g?y7|hrjpf#76l>#BRs4bf4o4~QtJYV=3HFEdQ( zzQ2Hniz^{L!G{$`l8xX6HyS{QVRZVla);6p><8f>PdJ~2=B%Ag-VQIwl7mT#&gq$g zq>gG+fs9njJLoub7v?a*JCrVvm+}GiC(J?KMN1Qq{-_63B}}ruD8Ldao#1#bOO+JM z*S|xsPmDeTVxysUe4+ytRI6q8TL7;BCRzK9m@=4)VAIb11sEvMoF9=T` z-Tof+hkEdWLK2`TxUt6?${@6xhkdMq z6d3OCW#|KMouu+L9yVD}3l`5p54+`WX$Z}*aTl7}@GTDv+_tG>5{{Ba7LUkur8ZMvrf*sewX*2 z3;9}1=nJvAAD)Cq&UA=#3>gow??P-+!xIUp=PA$(mx5wpXj6MEfC%r!YFlZ??+&6_ z0PVt?lN>kzO2ES731ypI(gk*j&Kw^c0LT+e0&Kq;?ZOg~WR@;KmA^-`7Dw%ob628B zvGIh+Urrh`SVfT`>&D?DOqL87tc-3fk^jMs1JEC*{Vh}MzE20f+^dNh{~l?l?2;`a z;7%dfE^4e3PH3+FNH~E6zmp;QOl}tgP}%F{7GmSmevA-VUQg;ij#tEf&}Xo&N9sjJ0@@C6XSsN3d~5PMpmlWc$uWrnqJn&Twkzz>K3& zafTNF!(j^qyVMBigd@gvykUy7_rc*#t@@a8P#3DZ0EUBx2=>k*3%SOaPj50yHS1hneED0fum=E?Z0iXmG&n0fXTFMn7R6Cyz)Trt~qz-v{V3oZg?s z6zoRD*;QwY0$yi>j#8il2Vf;L%6q1GQK}2))usBipsOA=gtp47=#uZPY9i7)0{5XP zR&l8UEAn(qL08Gg-1A^sR>qVbe_9*03=_0|x`k$Gam|sjMdMMg7B!`46vf~jC|EBE zi>h6(8#aV`e)^3rc|O7Y+8~TxV~TeLXfk+`K-K7;1cxMo{2Qwqp!rgjS>5NZIIU*N za4ZIer2UKOv4r9`9U(TLPRA>Bs)Le+T@LXCxqmCDBxUk(`{3cJH6te!1!*RfO_1kT zZ}aImVJH9rbV<_0WdA1*vYaVCV+6V|U0YoOmVWXyVmRKw;fV8hN;6eWn=gmhq(_5x zLnLRGy%^+SJN6fvwb8zduLS1DZ2sZca$d@|d zEmOwgjS`@!-~G0FlYn0d@=eJajDnydn8QfRSz)LhO(;__|7UEDmwDe^7+Qr-7aTT! ztI+hac0e&0+i2R}k9$|!tnE;YysfmG-cJWwtSixZ?Mwo}3gzcflM|N=%R{s1{`kkg zWw=Z(X~08lp!YyK)Ry;g8oiQ4AmNcn-=lA9Q`UB@?MGpDXuuzpM-CVCg*xBiXNy)g zy1**CrAq)JEUTsf%bZfS(Qf|yaxvI7+bv79a+9Ab$k_dHZN9|gGNEkP8o_?nq(1f8 z>=sRsfB`JSqiSFvbAAQ#MNt|Y{BoO3YhY{?t zVjJO{GUh=_)~fv%QxybzsTd=g+q~lVXx(UerqFF6j4%N;+1C`-*OHRGI?rsO2uudN z>k})LxVbqCGrtStB@Rx0{nz@|=Wh+iOAqjT`(sMh^HeJ**S|St75|%A>!ofLhm{k> zj&JoX`uoc-j|F&CP;n*0T!!&WCC|jWX4Z9Q4EtYr{5l&gd`vAh85RYC9jLuh;YVYy9=q$@NXMqY;OQLwO0{{|m^WA3fcl IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Nelmish/Assets/Default/TileMapIcon.png b/Projects/Nelmish/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Sand Box 2d/Assets/Default/RawIcon.png b/Projects/Sand Box 2d/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Sand Box 2d/Assets/Default/SongIcon.png b/Projects/Sand Box 2d/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Sand Box 2d/Assets/Default/SoundIcon.png b/Projects/Sand Box 2d/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Sand Box 2d/Assets/Default/SpineSkeletonIcon.png b/Projects/Sand Box 2d/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Sand Box 2d/Assets/Default/StaticModelIcon.png b/Projects/Sand Box 2d/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Sand Box 2d/Assets/Default/TileMapIcon.png b/Projects/Sand Box 2d/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Terra Firma/Assets/Default/RawIcon.png b/Projects/Terra Firma/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Terra Firma/Assets/Default/SongIcon.png b/Projects/Terra Firma/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Terra Firma/Assets/Default/SoundIcon.png b/Projects/Terra Firma/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Terra Firma/Assets/Default/SpineSkeletonIcon.png b/Projects/Terra Firma/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Terra Firma/Assets/Default/StaticModelIcon.png b/Projects/Terra Firma/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Terra Firma/Assets/Default/TileMapIcon.png b/Projects/Terra Firma/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 literal 0 HcmV?d00001 diff --git a/Projects/Twenty 48/Assets/Default/RawIcon.png b/Projects/Twenty 48/Assets/Default/RawIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..34c88aad5ef77d03ff2b090f52c8857d0c43add4 GIT binary patch literal 8265 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 V0-}Cl0RWNyJKcRL<*xfr{2O+o?D+ry literal 0 HcmV?d00001 diff --git a/Projects/Twenty 48/Assets/Default/SongIcon.png b/Projects/Twenty 48/Assets/Default/SongIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..06599ab761ae6245bfd9a0536a176bbd60ff255f GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^r$Ly54M-mNoo)=I7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gca%qgD@k*tT_@uHKCp^jv*CsZ|~kMx@92ZcJadZ`sZdX2A<3Z z=j;A-x63ZP=y!Q0kK1kcJf2!+pdkzl2@_90Uw{5@ZM}o(^yjfQ{}wttc;;^M|G1J! z#TUu5;W|!@_xG}Ye*W0psnK)$NhPrvC4yWjdAAJ`CPgTyh;ekXIwdtuKrjP-`+un4 z`CXoGdEwgGIs!u6Ek~FXH#&F_!#sU4=Kk68`T~u6VKpMb>zg0nSqCztdiU80FU2>0 zY+W`t;+u&%!@)ORI*SF*^GeVVKOjM$FUkfxh+BHYww_;#k2G~MipIOkw6Ti|pg zM_Or;rSQQuUOMT@Gp=?#?P1x>wQiEt)KON=Q zvOO<9O^R?+pK-RM)xD__Xw`;eU%M0JI-cspd|2uIH=#GOe%vCx@%n?XUpa?SA)%`KwBAh*uVo<0K-5v^sedY_Iqy`F{? zG1!>XpUr-->R$Mk?`NBB`>Xq(8y&LBsF|mBTIPClZT*L(Cpd$3I2%9s=>)qRQs4Mz z*(sAkqmIs}lH48YM?Ossc&7Pf{lcW5>!LLLcs6%DJ$ERvDPo;g+oi{r;=dcWMEHm} zZB1S&)g^pHNsaro$nB=stG!1<64fTR{^atTx_Z_|i`|cj!fW=v zmzuvjH=_IXA<4>*2Htxks(hz*O%$;=oT200vnRvn(9t7C;a88U8Eu@B@=#xC+Nx(P z%Io-(R=$xsxLRP@zPO%tH(qHk`yoEUj+f>Ck zu68dyURVFJ#2~aSZIe!E?2ng^=6^0YTc`2Ne>MtpETkse7~+tXR(gMLYbzMNs2;3r!RK?G&oXJdWBVUinHM9yBEcl z1@O7++?P#lc$fwKqxI;DCRi~w|<1Tycy8HO|6tRtaHeF>BJ1KBvQbe|4zNm8S zmA$WbxSlo?K3*qveOE+PMJbQ${Ko>^EkIs2W*);9m9R{>b$_r>mq8@9tO?O}*Y3(~ zUHuQYp1v)btz*Y2uXeNT;FR-;pDY4%PChujJ@)$h+xiy+W_kmQ6(DHvyLay&W3|$I T`_4xPqd+2_u6{1-oD!M<3GxkP literal 0 HcmV?d00001 diff --git a/Projects/Twenty 48/Assets/Default/SoundIcon.png b/Projects/Twenty 48/Assets/Default/SoundIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..19913af6ad5d7a67e39c9a84592ebd2617f62f99 GIT binary patch literal 3015 zcmb_ee>~Is8ds?>^6SKGib4|A(6CFKIYrXek6}a@Qbw2`nMC)jC^eLn%+BiK44Y03EUS`_d3&mHUYfoKiux%u2Cb&{pa8N&SPS%Z zM-KVzSNn4rY@3W=ZD3iQatw=GRvWKJ90ZzX;(?%KH8qH#N>-`S=uoh6^Vx^?BZqzx zzb&kA(p&rE?DF!%#X_9M_0y{iFx}hDnuh*nRgD_eSe{SV4i*29N4)BV++R*BJBsP6 zVxIQH?P1x#{x|a!Q@nJ!HLG2R-FFbtBK^eb>!40WIJ3QsZO43NEZE+tApUQ^u#S5E z{Od<)B}^xdOTP8iPd?`y)coQEOQA3eY!PBuiuZTlv

y6r&qW8=h}sDJ1@;{AW5W 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 literal 0 HcmV?d00001 diff --git a/Projects/Twenty 48/Assets/Default/SpineSkeletonIcon.png b/Projects/Twenty 48/Assets/Default/SpineSkeletonIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90b868eae930547322318a75eb96581853243f0 GIT binary patch literal 9180 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 aq(qAoSn48hodaLopuCiVNEW;>@cSPH&Gb|N literal 0 HcmV?d00001 diff --git a/Projects/Twenty 48/Assets/Default/StaticModelIcon.png b/Projects/Twenty 48/Assets/Default/StaticModelIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61fc44eca843d8c07ab6d313af747c84d3e759 GIT binary patch literal 6442 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 IVozWFANrs?^8f$< literal 0 HcmV?d00001 diff --git a/Projects/Twenty 48/Assets/Default/TileMapIcon.png b/Projects/Twenty 48/Assets/Default/TileMapIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe524e80d4a3e431dc92204ee74c848d401c0e43 GIT binary patch literal 6256 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 Date: Wed, 12 Nov 2025 22:41:19 -0500 Subject: [PATCH 33/52] Implemented #1211 + distance parameterization. --- Nu/Nu.Gaia/Gaia.fs | 2 +- Nu/Nu/Audio/AudioPlayer.fs | 34 +++++++++++++++++++++++-- Nu/Nu/World/WorldAudio.fs | 4 +-- Nu/Nu/World/WorldFacets.fs | 6 ++--- Nu/Nu/World/WorldImGui.fs | 8 +++--- Projects/Blaze Vector ImSim/Assets.fs | 4 +-- Projects/Blaze Vector ImSim/Enemy.fs | 4 +-- Projects/Blaze Vector ImSim/Gameplay.fs | 2 +- Projects/Blaze Vector ImSim/Player.fs | 4 +-- Projects/Blaze Vector Mmcc/Enemy.fs | 4 +-- Projects/Blaze Vector Mmcc/Player.fs | 6 ++--- Projects/Breakout ImSim/Gameplay.fs | 6 ++--- Projects/Breakout Mmcc/Gameplay.fs | 8 +++--- Projects/Terra Firma/Character.fs | 4 +-- Projects/Terra Firma/Gameplay.fs | 4 +-- 15 files changed, 65 insertions(+), 35 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index be1b8d0bd3..e52f23747a 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -831,7 +831,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= (* Editor Command Functions *) let private createRestorePoint world = - World.playSound Constants.Audio.SoundVolumeDefault Assets.Default.Sound world + World.playSound 0.0f 0.0f Constants.Audio.SoundVolumeDefault Assets.Default.Sound world if world.Advancing then World.setAdvancing false world snapshot RestorePoint world diff --git a/Nu/Nu/Audio/AudioPlayer.fs b/Nu/Nu/Audio/AudioPlayer.fs index b877b57feb..143e756287 100644 --- a/Nu/Nu/Audio/AudioPlayer.fs +++ b/Nu/Nu/Audio/AudioPlayer.fs @@ -10,9 +10,21 @@ open Prime /// Describes a sound. type SoundDescriptor = - { Volume : single + { Distance : single + Panning : single + Volume : single Sound : Sound AssetTag } + // OPTIMIZATION: instantiate once. + static let DefaultDescriptor = + { Distance = 0.0f + Panning = 0.0f + Volume = Constants.Audio.SoundVolumeDefault + Sound = asset Assets.Default.PackageName Assets.Default.SoundName } + + /// The default sound descriptor. + static member defaultDescriptor = DefaultDescriptor + /// Describes a song. type SongDescriptor = { FadeInTime : GameTime @@ -22,6 +34,18 @@ type SongDescriptor = Volume : single Song : Song AssetTag } + // OPTIMIZATION: instantiate once. + static let DefaultDescriptor = + { FadeInTime = GameTime.zero + FadeOutTime = Constants.Audio.FadeOutTimeDefault + StartTime = GameTime.zero + RepeatLimitOpt = None + Volume = Constants.Audio.SongVolumeDefault + Song = asset Assets.Default.PackageName Assets.Default.SongName } + + /// The default song descriptor. + static member defaultDescriptor = DefaultDescriptor + /// A message to the audio system. type AudioMessage = | LoadAudioPackageMessage of string @@ -276,7 +300,13 @@ type [] SdlAudioPlayer = match audioAsset with | WavAsset wavAsset -> SDL_mixer.Mix_VolumeChunk (wavAsset, int (playSoundMessage.Volume * audioPlayer.MasterSoundVolume * single SDL_mixer.MIX_MAX_VOLUME)) |> ignore - SDL_mixer.Mix_PlayChannel (-1, wavAsset, 0) |> ignore + let channel = SDL_mixer.Mix_PlayChannel (-1, wavAsset, 0) + let pan = playSoundMessage.Panning |> max -1.0f |> min 1.0f + let left = byte (255.0f * (1.0f - max 0.0f pan)) + let right = byte (255.0f * (1.0f + min 0.0f pan)) + SDL_mixer.Mix_SetPanning (channel, left, right) |> ignore + let distance = playSoundMessage.Distance |> max 0.0f |> min 1.0f |> (*) 255.0f |> byte + SDL_mixer.Mix_SetDistance (channel, distance) |> ignore | MusAsset _ -> Log.info ("Cannot play song asset as sound '" + scstring sound + "'.") | None -> Log.info ("PlaySoundMessage failed due to unloadable assets for '" + scstring sound + "'.") diff --git a/Nu/Nu/World/WorldAudio.fs b/Nu/Nu/World/WorldAudio.fs index a6668af462..a9af9a25bb 100644 --- a/Nu/Nu/World/WorldAudio.fs +++ b/Nu/Nu/World/WorldAudio.fs @@ -79,8 +79,8 @@ module WorldAudio = audioPlayer.SongFadingOut /// Send a message to the audio system to play a sound. - static member playSound volume sound world = - let playSoundMessage = PlaySoundMessage { Sound = sound; Volume = volume } + static member playSound distance panning volume sound world = + let playSoundMessage = PlaySoundMessage { Distance = distance; Panning = panning; Volume = volume; Sound = sound } World.enqueueAudioMessage playSoundMessage world /// Send a message to the audio system to play a song. diff --git a/Nu/Nu/World/WorldFacets.fs b/Nu/Nu/World/WorldFacets.fs index e24e3b0d81..2cd6835eee 100644 --- a/Nu/Nu/World/WorldFacets.fs +++ b/Nu/Nu/World/WorldFacets.fs @@ -583,7 +583,7 @@ type ButtonFacet () = let eventTrace = EventTrace.debug "ButtonFacet" "handleMouseLeftUp" "Click" EventTrace.empty World.publishPlus () entity.ClickEvent eventTrace entity true false world match entity.GetClickSoundOpt world with - | Some clickSound -> World.playSound (entity.GetClickSoundVolume world) clickSound world + | Some clickSound -> World.playSound 0.0f 0.0f (entity.GetClickSoundVolume world) clickSound world | None -> () Resolve else Cascade @@ -683,7 +683,7 @@ type ToggleButtonFacet () = let eventTrace = EventTrace.debug "ToggleFacet" "handleMouseLeftUp" "Toggle" EventTrace.empty World.publishPlus toggled entity.ToggleEvent eventTrace entity true false world match entity.GetToggleSoundOpt world with - | Some toggleSound -> World.playSound (entity.GetToggleSoundVolume world) toggleSound world + | Some toggleSound -> World.playSound 0.0f 0.0f (entity.GetToggleSoundVolume world) toggleSound world | None -> () Resolve else Cascade @@ -790,7 +790,7 @@ type RadioButtonFacet () = let eventTrace = EventTrace.debug "RadioButtonFacet" "handleMouseLeftUp" "Dial" EventTrace.empty World.publishPlus dialed entity.DialEvent eventTrace entity true false world match entity.GetDialSoundOpt world with - | Some dialSound -> World.playSound (entity.GetDialSoundVolume world) dialSound world + | Some dialSound -> World.playSound 0.0f 0.0f (entity.GetDialSoundVolume world) dialSound world | None -> () Resolve else Cascade diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index d4879a92cc..0fb4bb013e 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -386,8 +386,8 @@ module WorldImGui = elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|Assets.Default.Song :> obj|])) elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|Assets.Default.StaticModel :> obj|])) elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|Assets.Default.AnimatedModel :> obj|])) - elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|{ Volume = Constants.Audio.SongVolumeDefault; Sound = Assets.Default.Sound } :> obj|])) - elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|{ FadeInTime = GameTime.zero; FadeOutTime = Constants.Audio.FadeOutTimeDefault; StartTime = GameTime.zero; RepeatLimitOpt = None; Volume = Constants.Audio.SongVolumeDefault; Song = Assets.Default.Song } :> obj|])) + elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|SoundDescriptor.defaultDescriptor :> obj|])) + elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|SongDescriptor.defaultDescriptor :> obj|])) elif ty.GenericTypeArguments.[0] = typeof then (true, Activator.CreateInstance (ty, [|NoScatter :> obj|])) elif ty.GenericTypeArguments.[0].IsGenericType && ty.GenericTypeArguments.[0].GetGenericTypeDefinition () = typedefof<_ array> then (true, Activator.CreateInstance (ty, [|Reflection.objsToArray ty.GenericTypeArguments.[0] []|])) elif ty.GenericTypeArguments.[0].IsGenericType && ty.GenericTypeArguments.[0].GetGenericTypeDefinition () = typedefof<_ list> then (true, Activator.CreateInstance (ty, [|Reflection.objsToList ty.GenericTypeArguments.[0] []|])) @@ -478,8 +478,8 @@ module WorldImGui = elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption Assets.Default.Song) elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption Assets.Default.StaticModel) elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption Assets.Default.AnimatedModel) - elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption { Volume = Constants.Audio.SongVolumeDefault; Sound = Assets.Default.Sound }) - elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption { FadeInTime = GameTime.zero; FadeOutTime = Constants.Audio.FadeOutTimeDefault; StartTime = GameTime.zero; RepeatLimitOpt = None; Volume = Constants.Audio.SongVolumeDefault; Song = Assets.Default.Song }) + elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption SoundDescriptor.defaultDescriptor) + elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption SongDescriptor.defaultDescriptor) elif ty.GenericTypeArguments.[0] = typeof then (true, createValueOption NoScatter) elif ty.GenericTypeArguments.[0].IsGenericType && ty.GenericTypeArguments.[0].GetGenericTypeDefinition () = typedefof<_ array> then (true, createValueOption (Reflection.objsToArray ty.GenericTypeArguments.[0] [])) elif ty.GenericTypeArguments.[0].IsGenericType && ty.GenericTypeArguments.[0].GetGenericTypeDefinition () = typedefof<_ list> then (true, createValueOption (Reflection.objsToList ty.GenericTypeArguments.[0] [])) diff --git a/Projects/Blaze Vector ImSim/Assets.fs b/Projects/Blaze Vector ImSim/Assets.fs index 3798507f50..79453ac135 100644 --- a/Projects/Blaze Vector ImSim/Assets.fs +++ b/Projects/Blaze Vector ImSim/Assets.fs @@ -12,7 +12,7 @@ module Assets = module Gui = let PackageName = "Gui" - let MachinerySong = { FadeInTime = GameTime.zero; FadeOutTime = Constants.Audio.FadeOutTimeDefault; StartTime = GameTime.zero; RepeatLimitOpt = None; Volume = 1.0f; Song = asset PackageName "Machinery" } + let MachinerySong = { SongDescriptor.defaultDescriptor with Song = asset PackageName "Machinery" } let TitleGroupFilePath = "Assets/Gui/Title.nugroup" let CreditsGroupFilePath = "Assets/Gui/Credits.nugroup" @@ -30,7 +30,7 @@ module Assets = let ShotSound = asset PackageName "Shot" let JumpSound = asset PackageName "Jump" let DeathSound = asset PackageName "Death" - let DeadBlazeSong = { FadeInTime = GameTime.zero; FadeOutTime = Constants.Audio.FadeOutTimeDefault; StartTime = GameTime.zero; RepeatLimitOpt = None; Volume = 2.0f; Song = asset PackageName "DeadBlaze" } + let DeadBlazeSong = { SongDescriptor.defaultDescriptor with Volume = 2.0f; Song = asset PackageName "DeadBlaze" } let Section0FilePath = "Assets/Gameplay/Section0.nugroup" let Section1FilePath = "Assets/Gameplay/Section1.nugroup" let Section2FilePath = "Assets/Gameplay/Section2.nugroup" diff --git a/Projects/Blaze Vector ImSim/Enemy.fs b/Projects/Blaze Vector ImSim/Enemy.fs index a4d6487715..ba81536151 100644 --- a/Projects/Blaze Vector ImSim/Enemy.fs +++ b/Projects/Blaze Vector ImSim/Enemy.fs @@ -54,9 +54,9 @@ type EnemyDispatcher () = penetrations if Seq.notEmpty hits then entity.Health.Map dec world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.HitSound world + World.playSound 0.0f 0.5f 1.0f Assets.Gameplay.HitSound world // process death if entity.GetHealth world <= 0 then World.publish () entity.DeathEvent entity world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.ExplosionSound world \ No newline at end of file + World.playSound 0.0f 0.5f 1.0f Assets.Gameplay.ExplosionSound world \ No newline at end of file diff --git a/Projects/Blaze Vector ImSim/Gameplay.fs b/Projects/Blaze Vector ImSim/Gameplay.fs index 92f2d2ac79..8cef813784 100644 --- a/Projects/Blaze Vector ImSim/Gameplay.fs +++ b/Projects/Blaze Vector ImSim/Gameplay.fs @@ -82,7 +82,7 @@ type GameplayDispatcher () = // process player death if World.doSubscriptionAny "Death" player.DeathEvent world then match screen.GetGameplayState world with - | Playing -> World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.DeathSound world + | Playing -> World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.DeathSound world | Quit -> () // already in quit state, so no need to play sound again screen.SetGameplayState Quit world diff --git a/Projects/Blaze Vector ImSim/Player.fs b/Projects/Blaze Vector ImSim/Player.fs index 31b65e934c..3cae823c20 100644 --- a/Projects/Blaze Vector ImSim/Player.fs +++ b/Projects/Blaze Vector ImSim/Player.fs @@ -65,7 +65,7 @@ type PlayerDispatcher () = bullet.SetElevation (entity.GetElevation world) world bullet.SetCreationTime world.UpdateTime world World.applyBodyLinearImpulse (v3 Constants.Gameplay.BulletImpulse 0.0f 0.0f) None (bullet.GetBodyId world) world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.ShotSound world + World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.ShotSound world // process jumping if world.Advancing && @@ -74,7 +74,7 @@ type PlayerDispatcher () = World.isKeyboardKeyPressed KeyboardKey.Space world then entity.SetLastTimeJump world.UpdateTime world World.applyBodyLinearImpulse (v3 0.0f Constants.Gameplay.PlayerJumpForce 0.0f) None (entity.GetBodyId world) world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.JumpSound world + World.playSound 0.0f -0.5f 1.0f Assets.Gameplay.JumpSound world // process death if fallen then diff --git a/Projects/Blaze Vector Mmcc/Enemy.fs b/Projects/Blaze Vector Mmcc/Enemy.fs index 52781332d9..f92dbf70af 100644 --- a/Projects/Blaze Vector Mmcc/Enemy.fs +++ b/Projects/Blaze Vector Mmcc/Enemy.fs @@ -71,6 +71,6 @@ type EnemyDispatcher () = if enemy.Health <= 0 then World.publish () entity.DeathEvent entity world World.destroyEntity entity world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.ExplosionSound world + World.playSound 0.0f 0.5f 1.0f Assets.Gameplay.ExplosionSound world | Hit -> - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.HitSound world \ No newline at end of file + World.playSound 0.0f 0.5f 1.0f Assets.Gameplay.HitSound world \ No newline at end of file diff --git a/Projects/Blaze Vector Mmcc/Player.fs b/Projects/Blaze Vector Mmcc/Player.fs index 8025924b16..05027c8916 100644 --- a/Projects/Blaze Vector Mmcc/Player.fs +++ b/Projects/Blaze Vector Mmcc/Player.fs @@ -113,15 +113,15 @@ type PlayerDispatcher () = | Jump -> World.applyBodyLinearImpulse (v3 0.0f Constants.Gameplay.PlayerJumpForce 0.0f) None (entity.GetBodyId world) world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.JumpSound world + World.playSound 0.0f -0.5f 1.0f Assets.Gameplay.JumpSound world | Shoot -> let bullet = World.createEntity None NoOverlay None entity.Group world // OPTIMIZATION: NoOverlay to avoid reflection. bullet.SetPosition (entity.GetPosition world + v3 24.0f 1.0f 0.0f) world bullet.SetElevation (entity.GetElevation world) world World.applyBodyLinearImpulse (v3 Constants.Gameplay.BulletImpulse 0.0f 0.0f) None (bullet.GetBodyId world) world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.ShotSound world + World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.ShotSound world | Die -> World.publish () entity.DeathEvent entity world - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.DeathSound world \ No newline at end of file + World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.DeathSound world \ No newline at end of file diff --git a/Projects/Breakout ImSim/Gameplay.fs b/Projects/Breakout ImSim/Gameplay.fs index 700bced790..5e3b9eb89a 100644 --- a/Projects/Breakout ImSim/Gameplay.fs +++ b/Projects/Breakout ImSim/Gameplay.fs @@ -150,7 +150,7 @@ type GameplayDispatcher () = // paddle collision let bounce = (ball.GetPosition world - paddle.GetPosition world).Normalized * BallSpeed World.setBodyLinearVelocity bounce ballBodyId world - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world else @@ -161,7 +161,7 @@ type GameplayDispatcher () = World.setBodyLinearVelocity bounce ballBodyId world screen.Score.Map ((+) 100) world screen.Bricks.Map (Map.remove penetrateeId.BodySource.Name) world - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world // wall collision | (false, _) -> @@ -173,7 +173,7 @@ type GameplayDispatcher () = let velocity = ball.GetLinearVelocity world let bounce = velocity - 2.0f * Vector3.Dot (velocity, normal) * normal World.setBodyLinearVelocity bounce ballBodyId world - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world | _ -> () diff --git a/Projects/Breakout Mmcc/Gameplay.fs b/Projects/Breakout Mmcc/Gameplay.fs index 11eb457a58..b645c3179e 100644 --- a/Projects/Breakout Mmcc/Gameplay.fs +++ b/Projects/Breakout Mmcc/Gameplay.fs @@ -112,12 +112,12 @@ type [] Gameplay = let ball = if ball.PositionNext.X <= -160.0f || ball.PositionNext.X >= 160.0f then - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world { ball with Direction = ball.Direction.MapX negate } else ball let ball = if ball.PositionNext.Y >= 172.0f then - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world { ball with Direction = ball.Direction.MapY negate } else ball { gameplay with Ball = ball } @@ -129,7 +129,7 @@ type [] Gameplay = let ball = let perimeter = box3 (paddle.Position - paddle.Size * 0.5f) paddle.Size if perimeter.Intersects ball.PositionNext then - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world { ball with Direction = (ball.Position - paddle.Position).Normalized } else ball { gameplay with Ball = ball } @@ -144,7 +144,7 @@ type [] Gameplay = gameplay.Bricks let ball = if Map.notEmpty bricksIntersected then - World.playSound 1.0f Assets.Default.Sound world + World.playSound 0.0f 0.0f 1.0f Assets.Default.Sound world let brick = Seq.head bricksIntersected.Values { ball with Direction = (ball.Position - brick.Position).Normalized } else ball diff --git a/Projects/Terra Firma/Character.fs b/Projects/Terra Firma/Character.fs index 5c6c8e1902..2205e2f822 100644 --- a/Projects/Terra Firma/Character.fs +++ b/Projects/Terra Firma/Character.fs @@ -121,8 +121,8 @@ type CharacterDispatcher () = | AttackState attack -> let localTime = world.UpdateTime - attack.AttackTime match localTime with - | 7L -> World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.SlashSound world - | 67L -> World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.Slash2Sound world + | 7L -> World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.SlashSound world + | 67L -> World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.Slash2Sound world | _ -> () let (animationTime, animationName) = if localTime <= 55L diff --git a/Projects/Terra Firma/Gameplay.fs b/Projects/Terra Firma/Gameplay.fs index 7554aeb1e1..7f21efa97c 100644 --- a/Projects/Terra Firma/Gameplay.fs +++ b/Projects/Terra Firma/Gameplay.fs @@ -71,11 +71,11 @@ type GameplayDispatcher () = if not (character.GetActionState world).IsInjuryState then character.SetActionState (InjuryState { InjuryTime = world.UpdateTime }) world character.LinearVelocity.Map ((*) v3Up) world // zero out horizontal velocity on injury - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.InjureSound world + World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.InjureSound world elif not (character.GetActionState world).IsWoundState then character.SetActionState (WoundState { WoundTime = world.UpdateTime }) world character.LinearVelocity.Map ((*) v3Up) world // zero out horizontal velocity on wound - World.playSound Constants.Audio.SoundVolumeDefault Assets.Gameplay.InjureSound world + World.playSound 0.0f 0.0f 1.0f Assets.Gameplay.InjureSound world // process character deaths for character in characters do From 9ac8a096c28b38e7a416ab03c189ec68f9004e1f Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 13 Nov 2025 08:42:27 -0500 Subject: [PATCH 34/52] Implemented #966. Fixed C# warning. --- Nu/Nu.Gaia/Gaia.fs | 16 +++++----- Nu/Nu.Math/Nu.Math.csproj | 4 --- Nu/Nu/AssetGraph/AssetTag.fs | 8 ----- Nu/Nu/Assimp/Assimp.fs | 12 ++++---- Nu/Nu/Core/EventFilter.fs | 2 +- Nu/Nu/Core/Math.fs | 38 ------------------------ Nu/Nu/OpenGL/OpenGL.SpriteBatch.fs | 2 +- Nu/Nu/Physics/AetherPhysicsEngine.fs | 2 +- Nu/Nu/Render/Renderer2d.fs | 2 +- Nu/Nu/Render/Renderer3d.fs | 14 ++++----- Nu/Nu/Render/RendererPrelude.fs | 6 ++-- Nu/Nu/Transform/Presence.fs | 19 +----------- Nu/Nu/Transform/Transform.fs | 4 +-- Nu/Nu/World/WorldContent.fs | 4 +-- Nu/Nu/World/WorldEntity.fs | 8 ++--- Nu/Nu/World/WorldFacets.fs | 10 +++---- Nu/Nu/World/WorldImGui.fs | 12 ++++---- Nu/Nu/World/WorldModule2.fs | 8 ++--- Nu/Nu/World/WorldModuleEntity.fs | 44 ++++++++++++++-------------- Nu/Nu/World/WorldModuleGame.fs | 8 ++--- 20 files changed, 78 insertions(+), 145 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index e52f23747a..d6789d806c 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1944,7 +1944,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let dispatcherNames = (World.getEntityDispatchers world).Keys let dispatcherNamePicked = tryPickName dispatcherNames for dispatcherName in dispatcherNames do - if ImGui.Selectable (dispatcherName, strEq dispatcherName dispatcherNameCurrent) then + if ImGui.Selectable (dispatcherName, (dispatcherName = dispatcherNameCurrent)) then if not (entity.GetProtected world) then snapshot ChangeEntityDispatcher world World.changeEntityDispatcher dispatcherName entity world @@ -1965,7 +1965,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if ImGui.BeginCombo ("Facet Name " + string i, facetName, ImGuiComboFlags.HeightRegular) then let facetNameSelectablePicked = tryPickName facetNamesSelectable for facetNameSelectable in facetNamesSelectable do - if ImGui.Selectable (facetNameSelectable, strEq facetName NewEntityDispatcherName) then + if ImGui.Selectable (facetNameSelectable, (facetName = NewEntityDispatcherName)) then facetName <- facetNameSelectable edited <- true if Some facetNameSelectable = facetNameSelectablePicked then ImGui.SetScrollHereY Constants.Gaia.HeightRegularPickOffset @@ -2501,7 +2501,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let dispatcherNames = (World.getEntityDispatchers world).Keys let dispatcherNamePicked = tryPickName dispatcherNames for dispatcherName in dispatcherNames do - if ImGui.Selectable (dispatcherName, strEq dispatcherName NewEntityDispatcherName) then NewEntityDispatcherName <- dispatcherName + if ImGui.Selectable (dispatcherName, (dispatcherName = NewEntityDispatcherName)) then NewEntityDispatcherName <- dispatcherName if Some dispatcherName = dispatcherNamePicked then ImGui.SetScrollHereY Constants.Gaia.HeightRegularPickOffset if dispatcherName = NewEntityDispatcherName then ImGui.SetItemDefaultFocus () ImGui.EndCombo () @@ -2513,7 +2513,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if ImGui.BeginCombo ("##newEntityOverlayName", NewEntityOverlayName, ImGuiComboFlags.HeightRegular) then let overlayNamePicked = tryPickName overlayNames for overlayName in overlayNames do - if ImGui.Selectable (overlayName, strEq overlayName NewEntityOverlayName) then NewEntityOverlayName <- overlayName + if ImGui.Selectable (overlayName, (overlayName = NewEntityOverlayName)) then NewEntityOverlayName <- overlayName if Some overlayName = overlayNamePicked then ImGui.SetScrollHereY Constants.Gaia.HeightRegularPickOffset if overlayName = NewEntityOverlayName then ImGui.SetItemDefaultFocus () ImGui.EndCombo () @@ -2568,7 +2568,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if ImGui.BeginCombo ("##projectEditMode", ProjectEditMode, ImGuiComboFlags.HeightRegular) then let editModes = World.getEditModes world for (editModeName, editModeFn) in editModes.Pairs do - if ImGui.Selectable (editModeName, strEq editModeName ProjectEditMode) then + if ImGui.Selectable (editModeName, (editModeName = ProjectEditMode)) then ProjectEditMode <- editModeName snapshot (SetEditMode 0) world // snapshot before mode change selectEntityOpt None world @@ -2682,7 +2682,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.SetNextItemWidth -1.0f if ImGui.BeginCombo ("##selectedGroupName", selectedGroupName, ImGuiComboFlags.HeightRegular) then for group in groups do - if ImGui.Selectable (group.Name, strEq group.Name selectedGroupName) then + if ImGui.Selectable (group.Name, group.Name = selectedGroupName) then selectEntityOpt None world selectGroup true group if group.Name = selectedGroupName then ImGui.SetItemDefaultFocus () @@ -3656,7 +3656,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let dispatcherNames = (World.getGroupDispatchers world).Keys let dispatcherNamePicked = tryPickName dispatcherNames for dispatcherName in dispatcherNames do - if ImGui.Selectable (dispatcherName, strEq dispatcherName NewGroupDispatcherName) then NewGroupDispatcherName <- dispatcherName + if ImGui.Selectable (dispatcherName, (dispatcherName = NewGroupDispatcherName)) then NewGroupDispatcherName <- dispatcherName if Some dispatcherName = dispatcherNamePicked then ImGui.SetScrollHereY Constants.Gaia.HeightRegularPickOffset if dispatcherName = NewGroupDispatcherName then ImGui.SetItemDefaultFocus () ImGui.EndCombo () @@ -3877,7 +3877,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let dispatcherNames = (World.getEntityDispatchers world).Keys let dispatcherNamePicked = tryPickName dispatcherNames for dispatcherName in dispatcherNames do - if ImGui.Selectable (dispatcherName, strEq dispatcherName NewEntityDispatcherName) then + if ImGui.Selectable (dispatcherName, (dispatcherName = NewEntityDispatcherName)) then NewEntityDispatcherName <- dispatcherName if Some dispatcherName = dispatcherNamePicked then ImGui.SetScrollHereY Constants.Gaia.HeightRegularPickOffset if dispatcherName = NewEntityDispatcherName then ImGui.SetItemDefaultFocus () diff --git a/Nu/Nu.Math/Nu.Math.csproj b/Nu/Nu.Math/Nu.Math.csproj index 7b483cce7f..3746adf475 100644 --- a/Nu/Nu.Math/Nu.Math.csproj +++ b/Nu/Nu.Math/Nu.Math.csproj @@ -21,8 +21,4 @@ 7 - - - - \ No newline at end of file diff --git a/Nu/Nu/AssetGraph/AssetTag.fs b/Nu/Nu/AssetGraph/AssetTag.fs index 9a5199865c..1e6118c159 100644 --- a/Nu/Nu/AssetGraph/AssetTag.fs +++ b/Nu/Nu/AssetGraph/AssetTag.fs @@ -128,14 +128,6 @@ module AssetTag = [] module AssetTagOperators = - /// Check two asset tags for equality. - let inline assetEq (left : AssetTag) (right : AssetTag) = - left = right - - /// Check two asset tags for inequality. - let inline assetNeq left right = - not (assetEq left right) - /// Make an asset tag. let asset<'a> packageName assetName : 'a AssetTag = AssetTag.make packageName assetName \ No newline at end of file diff --git a/Nu/Nu/Assimp/Assimp.fs b/Nu/Nu/Assimp/Assimp.fs index 01a6df277e..a8429982cd 100644 --- a/Nu/Nu/Assimp/Assimp.fs +++ b/Nu/Nu/Assimp/Assimp.fs @@ -228,12 +228,6 @@ module AssimpExtensions = NodeName : string HashCode : int } - static member make animationName nodeName = - let hashCode = hash animationName ^^^ hash nodeName - { AnimationName = animationName - NodeName = nodeName - HashCode = hashCode } - static member equals left right = left.AnimationName = right.AnimationName && left.NodeName = right.NodeName @@ -241,6 +235,12 @@ module AssimpExtensions = static member hash key = key.HashCode + static member make animationName nodeName = + let hashCode = hash animationName ^^^ hash nodeName + { AnimationName = animationName + NodeName = nodeName + HashCode = hashCode } + override this.Equals thatObj = match thatObj with | :? AnimationChannelKey as that -> AnimationChannelKey.equals this that diff --git a/Nu/Nu/Core/EventFilter.fs b/Nu/Nu/Core/EventFilter.fs index f0d84e4bd9..cfd8f2137e 100644 --- a/Nu/Nu/Core/EventFilter.fs +++ b/Nu/Nu/Core/EventFilter.fs @@ -47,7 +47,7 @@ and [)>] Rexpr (pattern) = member this.Pattern = pattern override this.Equals that = match that with - | :? Rexpr as that -> strEq this.Pattern that.Pattern + | :? Rexpr as that -> this.Pattern = that.Pattern | _ -> false override this.GetHashCode () = hash pattern diff --git a/Nu/Nu/Core/Math.fs b/Nu/Nu/Core/Math.fs index b885e9ed95..c5498f0413 100644 --- a/Nu/Nu/Core/Math.fs +++ b/Nu/Nu/Core/Math.fs @@ -61,8 +61,6 @@ module Vector2 = a.Rotate r let inline v2 x y = Vector2 (x, y) - let inline v2Eq (v : Vector2) (v2 : Vector2) = v.X = v2.X && v.Y = v2.Y - let inline v2Neq (v : Vector2) (v2 : Vector2) = v.X <> v2.X || v.Y <> v2.Y let v2EqApprox (v : Vector2) (v2 : Vector2) epsilon = Math.ApproximatelyEqual (v.X, v2.X, epsilon) && Math.ApproximatelyEqual (v.Y, v2.Y, epsilon) @@ -200,8 +198,6 @@ module Vector3 = -Vector3.ToPlane (v, p) let inline v3 x y z = Vector3 (x, y, z) - let inline v3Eq (v : Vector3) (v2 : Vector3) = v.X = v2.X && v.Y = v2.Y && v.Z = v2.Z - let inline v3Neq (v : Vector3) (v2 : Vector3) = v.X <> v2.X || v.Y <> v2.Y || v.Z <> v2.Z let v3EqApprox (v : Vector3) (v2 : Vector3) epsilon = Math.ApproximatelyEqual (v.X, v2.X, epsilon) && Math.ApproximatelyEqual (v.Y, v2.Y, epsilon) && @@ -295,8 +291,6 @@ module Vector4 = a.W % b.W) let inline v4 x y z w = Vector4 (x, y, z, w) - let inline v4Eq (v : Vector4) (v2 : Vector4) = v.X = v2.X && v.Y = v2.Y && v.Z = v2.Z && v.W = v2.W - let inline v4Neq (v : Vector4) (v2 : Vector4) = v.X <> v2.X || v.Y <> v2.Y || v.Z <> v2.Z || v.W <> v2.W let v4EqApprox (v : Vector4) (v2 : Vector4) epsilon = Math.ApproximatelyEqual (v.X, v2.X, epsilon) && Math.ApproximatelyEqual (v.Y, v2.Y, epsilon) && @@ -370,8 +364,6 @@ module Vector2i = a.Y % b.Y) let inline v2i x y = Vector2i (x, y) - let inline v2iEq (v : Vector2i) (v2 : Vector2i) = v.X = v2.X && v.Y = v2.Y - let inline v2iNeq (v : Vector2i) (v2 : Vector2i) = v.X <> v2.X || v.Y <> v2.Y let inline v2iDup (a : int) = v2i a a let v2iOne = Vector2i.One let v2iZero = Vector2i.Zero @@ -440,8 +432,6 @@ module Vector3i = a.Z % b.Z) let inline v3i x y z = Vector3i (x, y, z) - let inline v3iEq (v : Vector3i) (v2 : Vector3i) = v.X = v2.X && v.Y = v2.Y && v.Z = v2.Z - let inline v3iNeq (v : Vector3i) (v2 : Vector3i) = v.X <> v2.X || v.Y <> v2.Y || v.Z <> v2.Z let inline v3iDup (a : int) = v3i a a a let v3iOne = Vector3i.One let v3iZero = Vector3i.Zero @@ -521,8 +511,6 @@ module Vector4i = a.W % b.W) let inline v4i x y z w = Vector4i (x, y, z, w) - let inline v4iEq (v : Vector4i) (v2 : Vector4i) = v.X = v2.X && v.Y = v2.Y && v.Z = v2.Z && v.W = v2.W - let inline v4iNeq (v : Vector4i) (v2 : Vector4i) = v.X <> v2.X || v.Y <> v2.Y || v.Z <> v2.Z || v.W <> v2.W let inline v4iDup (a : int) = v4i a a a a let v4iOne = Vector4i.One let v4iZero = Vector4i.Zero @@ -663,8 +651,6 @@ module Quaternion = let quatIdentity = Quaternion.Identity let inline quat x y z w = Quaternion (x, y, z, w) - let inline quatEq (q : Quaternion) (q2 : Quaternion) = q.Equals q2 - let inline quatNeq (q : Quaternion) (q2 : Quaternion) = not (q.Equals q2) let quatEqApprox (v : Quaternion) (v2 : Quaternion) epsilon = Math.ApproximatelyEqual (v.X, v2.X, epsilon) && Math.ApproximatelyEqual (v.Y, v2.Y, epsilon) && @@ -735,8 +721,6 @@ module Box2 = let box2Zero = Box2.Zero let inline box2 min size = Box2 (min, size) - let inline box2Eq (b : Box2) (b2 : Box2) = b.Equals b2 - let inline box2Neq (b : Box2) (b2 : Box2) = not (b.Equals b2) let box2Slice sliceIndex (sliceMargin : Vector2) (perimeter : Box2) = match sliceIndex with @@ -881,8 +865,6 @@ module Box3 = let box3Zero = Box3.Zero let inline box3 min size = Box3 (min, size) - let inline box3Eq (b : Box3) (b2 : Box3) = b.Equals b2 - let inline box3Neq (b : Box3) (b2 : Box3) = not (b.Equals b2) let box3Slice sliceIndex sliceMargins (perimeter : Box3) = (box2Slice sliceIndex sliceMargins perimeter.Box2).Box3 let box3SliceInverted sliceIndex sliceMargins (perimeter : Box3) = (box2SliceInverted sliceIndex sliceMargins perimeter.Box2).Box3 @@ -948,8 +930,6 @@ module Box2i = let box2iZero = Box2i.Zero let inline box2i min size = Box2i (min, size) - let inline box2iEq (b : Box2i) (b2 : Box2i) = b.Equals b2 - let inline box2iNeq (b : Box2i) (b2 : Box2i) = not (b.Equals b2) /// Converts Box2i types. type Box2iConverter () = @@ -1014,8 +994,6 @@ module Box3i = let box3iZero = Box3i.Zero let inline box3i min size = Box3i (min, size) - let inline box3iEq (b : Box3i) (b2 : Box3i) = b.Equals b2 - let inline box3iNeq (b : Box3i) (b2 : Box3i) = not (b.Equals b2) /// Converts Box3i types. type Box3iConverter () = @@ -1128,8 +1106,6 @@ module Matrix3x2 = r1.X, r1.Y, r2.X, r2.Y) - let inline m3x2Eq (x : Matrix3x2) (y : Matrix3x2) = x.Equals y - let inline m3x2Neq (x : Matrix3x2) (y : Matrix3x2) = not (x.Equals y) let m3x2Identity = Matrix3x2.Identity let m3x2Zero = Unchecked.defaultof @@ -1258,8 +1234,6 @@ module Matrix4x4 = r2.X, r2.Y, r2.Z, r2.W, r3.X, r3.Y, r3.Z, r3.W) - let inline m4Eq (x : Matrix4x4) (y : Matrix4x4) = x.Equals y - let inline m4Neq (x : Matrix4x4) (y : Matrix4x4) = not (x.Equals y) let m4Identity = Matrix4x4.Identity let m4Zero = Unchecked.defaultof @@ -1328,8 +1302,6 @@ module Color = let inline color8 (r : byte) (g : byte) (b : byte) (a : byte) = Color (r, g, b, a) let inline color8Dup (a : byte) = color8 a a a a let inline colorPacked (u : uint) = Color u - let inline colorEq (x : Color) (y : Color) = x.R = y.R && x.G = y.G && x.B = y.B && x.A = y.A - let inline colorNeq (x : Color) (y : Color) = x.R <> y.R || x.G <> y.G || x.B <> y.B || x.A <> y.A let colorZero = Color.Zero let colorOne = Color.One @@ -1410,8 +1382,6 @@ type Segment2Converter () = module Segment2 = let inline segment2 (a : Vector2) (b : Vector2) = Segment2 (a, b) - let inline segment2Eq (left : Segment2) (right : Segment2) = left.Equals right - let inline segment2Neq (left : Segment2) (right : Segment2) = not (left.Equals right) type Segment2 with member this.Magnitude = this.Length () @@ -1457,8 +1427,6 @@ type Segment3Converter () = module Segment3 = let inline segment3 (a : Vector3) (b : Vector3) = Segment3 (a, b) - let inline segment3Eq (left : Segment3) (right : Segment3) = left.Equals right - let inline segment3Neq (left : Segment3) (right : Segment3) = not (left.Equals right) type Segment3 with member this.Magnitude = this.Length () @@ -1469,16 +1437,12 @@ module Segment3 = module Ray2 = let inline ray2 (origin : Vector2) (direction : Vector2) = Ray2 (origin, direction) - let inline ray2Eq (left : Ray2) (right : Ray2) = left.Equals right - let inline ray2Neq (left : Ray2) (right : Ray2) = not (left.Equals right) // TODO: create symbolic converter for Ray3. [] module Ray3 = let inline ray3 (origin : Vector3) (direction : Vector3) = Ray3 (origin, direction) - let inline ray3Eq (left : Ray3) (right : Ray3) = left.Equals right - let inline ray3Neq (left : Ray3) (right : Ray3) = not (left.Equals right) // TODO: create symbolic converter for Plane3. [] @@ -1486,8 +1450,6 @@ module Plane3 = let inline plane3 (pointOnPlane : Vector3) (normal : Vector3) = Plane3 (pointOnPlane, normal) let inline plane3Equation (normal : Vector3) (d : single) = Plane3 (normal, d) - let inline plane3Eq (left : Plane3) (right : Plane3) = left.Equals right - let inline plane3Neq (left : Plane3) (right : Plane3) = not (left.Equals right) type Plane3 with diff --git a/Nu/Nu/OpenGL/OpenGL.SpriteBatch.fs b/Nu/Nu/OpenGL/OpenGL.SpriteBatch.fs index b1d8714754..676cc0c9b4 100644 --- a/Nu/Nu/OpenGL/OpenGL.SpriteBatch.fs +++ b/Nu/Nu/OpenGL/OpenGL.SpriteBatch.fs @@ -24,7 +24,7 @@ module SpriteBatch = | struct (ValueSome _, ValueNone) -> true | struct (ValueNone, ValueSome _) -> true | struct (ValueNone, ValueNone) -> false - | struct (ValueSome c, ValueSome c2) -> box2Neq c c2) || + | struct (ValueSome c, ValueSome c2) -> c <> c2) || state.BlendingFactorSrc <> state2.BlendingFactorSrc || state.BlendingFactorDst <> state2.BlendingFactorDst || state.BlendingEquation <> state2.BlendingEquation || diff --git a/Nu/Nu/Physics/AetherPhysicsEngine.fs b/Nu/Nu/Physics/AetherPhysicsEngine.fs index a4169c6584..d99628179b 100644 --- a/Nu/Nu/Physics/AetherPhysicsEngine.fs +++ b/Nu/Nu/Physics/AetherPhysicsEngine.fs @@ -706,7 +706,7 @@ and [] AetherPhysicsEngine = static member private attachBoxRoundedShape bodySource (bodyProperties : BodyProperties) (boxRoundedShape : BoxRoundedShape) (body : Body) = let transform = Option.mapOrDefaultValue (fun (a : Affine) -> let mutable t = a in t.Matrix) m4Identity boxRoundedShape.TransformOpt - if quatNeq transform.Rotation quatIdentity then Log.warnOnce "BoxRoundedShape rotation not yet supported by AetherPhysicsEngine." // TODO: implement! + if transform.Rotation <> quatIdentity then Log.warnOnce "BoxRoundedShape rotation not yet supported by AetherPhysicsEngine." // TODO: implement! let width = AetherPhysicsEngine.toPhysicsPolygonDiameter (boxRoundedShape.Size.X * transform.Scale.X) let height = AetherPhysicsEngine.toPhysicsPolygonDiameter (boxRoundedShape.Size.Y * transform.Scale.Y) let radius = AetherPhysicsEngine.toPhysicsPolygonRadius (boxRoundedShape.Radius * transform.Scale.X) diff --git a/Nu/Nu/Render/Renderer2d.fs b/Nu/Nu/Render/Renderer2d.fs index 04ffc2c6d8..9e7dd7a1ec 100644 --- a/Nu/Nu/Render/Renderer2d.fs +++ b/Nu/Nu/Render/Renderer2d.fs @@ -321,7 +321,7 @@ type [] GlRenderer2d = static member private tryGetRenderAsset (assetTag : AssetTag) renderer = let mutable assetInfo = Unchecked.defaultof // OPTIMIZATION: seems like TryGetValue allocates here if we use the tupling idiom (this may only be the case in Debug builds tho). if renderer.RenderAssetCached.CachedAssetTagOpt :> obj |> notNull && - assetEq assetTag renderer.RenderAssetCached.CachedAssetTagOpt then + assetTag = renderer.RenderAssetCached.CachedAssetTagOpt then renderer.RenderAssetCached.CachedAssetTagOpt <- assetTag // NOTE: this isn't redundant because we want to trigger refEq early-out. ValueSome renderer.RenderAssetCached.CachedRenderAsset elif diff --git a/Nu/Nu/Render/Renderer3d.fs b/Nu/Nu/Render/Renderer3d.fs index fae3d0a487..d9a2dc0f32 100644 --- a/Nu/Nu/Render/Renderer3d.fs +++ b/Nu/Nu/Render/Renderer3d.fs @@ -975,7 +975,7 @@ type [] private AnimatedModelSurfaceKey = let mutable equal = true let mutable i = 0 while i < left.BoneTransforms.Length && equal do - equal <- m4Eq left.BoneTransforms.[i] right.BoneTransforms.[i] + equal <- left.BoneTransforms.[i] = right.BoneTransforms.[i] i <- inc i equal && OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.equals left.AnimatedSurface right.AnimatedSurface else false @@ -1079,7 +1079,7 @@ type [] private RenderTasks = OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.equals static_.Key staticCached.Key && static_.Value.Count = staticCached.Value.Count && (static_.Value, staticCached.Value) - ||> Seq.forall2 (fun struct (m, cs, _, _, _) struct (mCached, csCached, _, _, _) -> m4Eq m mCached && cs = csCached)) + ||> Seq.forall2 (fun struct (m, cs, _, _, _) struct (mCached, csCached, _, _, _) -> m = mCached && cs = csCached)) let deferredStaticPreBatchesCached = renderTasks.DeferredStaticPreBatches.Count = renderTasksCached.DeferredStaticPreBatches.Count && (renderTasks.DeferredStaticPreBatches, renderTasksCached.DeferredStaticPreBatches) @@ -1091,7 +1091,7 @@ type [] private RenderTasks = OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.equals static_.Key staticCached.Key && static_.Value.Count = staticCached.Value.Count && (static_.Value, staticCached.Value) - ||> Seq.forall2 (fun struct (m, cs, _, _, _) struct (mCached, csCached, _, _, _) -> m4Eq m mCached && cs = csCached)) + ||> Seq.forall2 (fun struct (m, cs, _, _, _) struct (mCached, csCached, _, _, _) -> m = mCached && cs = csCached)) let deferredStaticClippedPreBatchesCached = renderTasks.DeferredStaticClippedPreBatches.Count = renderTasksCached.DeferredStaticClippedPreBatches.Count && (renderTasks.DeferredStaticClippedPreBatches, renderTasksCached.DeferredStaticClippedPreBatches) @@ -1103,20 +1103,20 @@ type [] private RenderTasks = AnimatedModelSurfaceKey.equals animated.Key animatedCached.Key && animated.Value.Count = animatedCached.Value.Count && (animated.Value, animatedCached.Value) - ||> Seq.forall2 (fun struct (m, cs, _, _, _) struct (mCached, csCached, _, _, _) -> m4Eq m mCached && cs = csCached)) + ||> Seq.forall2 (fun struct (m, cs, _, _, _) struct (mCached, csCached, _, _, _) -> m = mCached && cs = csCached)) let deferredTerrainsCached = renderTasks.DeferredTerrains.Count = renderTasksCached.DeferredTerrains.Count && (renderTasks.DeferredTerrains, renderTasksCached.DeferredTerrains) ||> Seq.forall2 (fun struct (terrainDescriptor, patchDescriptor, _) struct (terrainDescriptorCached, patchDescriptorCached, _) -> patchDescriptor = patchDescriptorCached && - box3Eq terrainDescriptor.Bounds terrainDescriptorCached.Bounds && + terrainDescriptor.Bounds = terrainDescriptorCached.Bounds && terrainDescriptor.CastShadow = terrainDescriptorCached.CastShadow && terrainDescriptor.HeightMap = terrainDescriptorCached.HeightMap) let forwardCached = renderTasks.Forward.Count = renderTasksCached.Forward.Count && (renderTasks.Forward, renderTasksCached.Forward) ||> Seq.forall2 (fun struct (_, _, m, cs, _, _, _, bo, s, _) struct (_, _, mCached, csCached, _, _, _, boCached, sCached, _) -> - m4Eq m mCached && + m = mCached && cs = csCached && bo = boCached && // TODO: P0: optimize? OpenGL.PhysicallyBased.PhysicallyBasedSurfaceFns.equals s sCached) @@ -1500,7 +1500,7 @@ type [] GlRenderer3d = static member private tryGetRenderAsset (assetTag : AssetTag) renderer = let mutable assetInfo = Unchecked.defaultof // OPTIMIZATION: seems like TryGetValue allocates here if we use the tupling idiom (this may only be the case in Debug builds tho). if renderer.RenderAssetCached.CachedAssetTagOpt :> obj |> notNull && - assetEq assetTag renderer.RenderAssetCached.CachedAssetTagOpt then + assetTag = renderer.RenderAssetCached.CachedAssetTagOpt then renderer.RenderAssetCached.CachedAssetTagOpt <- assetTag // NOTE: this isn't redundant because we want to trigger refEq early-out. ValueSome renderer.RenderAssetCached.CachedRenderAsset elif diff --git a/Nu/Nu/Render/RendererPrelude.fs b/Nu/Nu/Render/RendererPrelude.fs index cba41873a7..cba2ef8051 100644 --- a/Nu/Nu/Render/RendererPrelude.fs +++ b/Nu/Nu/Render/RendererPrelude.fs @@ -78,7 +78,7 @@ type [] RenderPass = match this with | LightMapPass (id, bounds) -> match that with - | LightMapPass (id2, bounds2) -> id = id2 && box3Eq bounds bounds2 + | LightMapPass (id2, bounds2) -> id = id2 && bounds = bounds2 | _ -> false | ShadowPass (id, indexInfoOpt, lightType, rotation, frustum) -> match that with @@ -87,11 +87,11 @@ type [] RenderPass = (match indexInfoOpt with | Some (index, view, projection) -> match indexInfoOpt2 with - | Some (index2, view2, projection2) -> index = index2 && m4Eq view view2 && m4Eq projection projection2 + | Some (index2, view2, projection2) -> index = index2 && view = view2 && projection = projection2 | None -> false | None -> indexInfoOpt2.IsNone) && lightType = lightType2 && - quatEq rotation rotation2 && + rotation = rotation2 && frustum = frustum2 | _ -> false | ReflectionPass (id, frustum) -> diff --git a/Nu/Nu/Transform/Presence.fs b/Nu/Nu/Transform/Presence.fs index 998c523057..ce939c069a 100644 --- a/Nu/Nu/Transform/Presence.fs +++ b/Nu/Nu/Transform/Presence.fs @@ -52,21 +52,4 @@ type [] Presence = | Interior -> match frustumInteriorOpt with ValueSome frustumInterior -> frustumInterior.Intersects bounds | ValueNone -> false | Exterior -> frustumExterior.Intersects bounds || (match frustumInteriorOpt with ValueSome frustumInterior -> frustumInterior.Intersects bounds | ValueNone -> false) | Imposter -> frustumImposter.Intersects bounds - | Omnipresent -> true - -/// Presence operators. -[] -module PresenceOperators = - - /// Test two presence values for equality without allocating. - let presenceEq left right = - match struct (left, right) with - | struct (Interior, Interior) - | struct (Exterior, Exterior) - | struct (Imposter, Imposter) - | struct (Omnipresent, Omnipresent) -> true - | struct (_, _) -> false - - /// Test two presence values for inequality. - let inline presenceNeq left right = - not (presenceEq left right) \ No newline at end of file + | Omnipresent -> true \ No newline at end of file diff --git a/Nu/Nu/Transform/Transform.fs b/Nu/Nu/Transform/Transform.fs index 676fadc019..7a7c4f06bf 100644 --- a/Nu/Nu/Transform/Transform.fs +++ b/Nu/Nu/Transform/Transform.fs @@ -325,8 +325,8 @@ type [] Transform = /// Test transforms for equality. static member equalsByRef (left : Transform inref, right : Transform inref) = left.Flags_ = right.Flags_ && - v3Eq left.Position_ right.Position_ && - quatEq left.Rotation_ right.Rotation_ && + left.Position_ = right.Position_ && + left.Rotation_ = right.Rotation_ && v3EqApprox left.Scale_ right.Scale_ 0.0001f && // NOTE: using approx here since scale tends to be pulled from an affine matrix. Also, just guessing at espilon... left.Offset_.Equals right.Offset_ && left.Size_.Equals right.Size_ && diff --git a/Nu/Nu/World/WorldContent.fs b/Nu/Nu/World/WorldContent.fs index 05ae67150c..84d7e848fc 100644 --- a/Nu/Nu/World/WorldContent.fs +++ b/Nu/Nu/World/WorldContent.fs @@ -163,7 +163,7 @@ module Content = let childrenAdded = List () for childEntry in childContents do match childContentsOld.TryGetValue childEntry.Key with - | (true, childContentOld) when optEq childEntry.Value.DispatcherNameOpt childContentOld.DispatcherNameOpt -> + | (true, childContentOld) when childEntry.Value.DispatcherNameOpt = childContentOld.DispatcherNameOpt -> let childSimulant = // OPTIMIZATION: attempt to get child simulant from old content rather than deriving it, and store it for future use. if isNull (childContentOld.SimulantCachedOpt :> obj) then let derived = World.deriveFromNames (Array.add childEntry.Key simulant.SimulantAddress.Names) :?> 'child @@ -181,7 +181,7 @@ module Content = let childrenRemoved = List<'child> () for childEntryOld in childContentsOld do match childContents.TryGetValue childEntryOld.Key with - | (true, childContentOld) when optEq childEntryOld.Value.DispatcherNameOpt childContentOld.DispatcherNameOpt -> () + | (true, childContentOld) when childEntryOld.Value.DispatcherNameOpt = childContentOld.DispatcherNameOpt -> () | (_, _) -> let childSimulant = childEntryOld.Value.SimulantCachedOpt :?> 'child // OPTIMIZATION: because of above optimization, should be guaranteed to exist. childrenRemoved.Add childSimulant diff --git a/Nu/Nu/World/WorldEntity.fs b/Nu/Nu/World/WorldEntity.fs index deb302d053..a6e75a7848 100644 --- a/Nu/Nu/World/WorldEntity.fs +++ b/Nu/Nu/World/WorldEntity.fs @@ -516,14 +516,14 @@ module WorldEntityModule = let mutable transformOld = this.GetTransform world let mutable transformNew = transformOld if this.GetIs2d world then - if v3Neq transformOld.PerimeterCenter center || - quatNeq transformOld.Rotation rotation then + if transformOld.PerimeterCenter <> center || + transformOld.Rotation <> rotation then transformNew.PerimeterCenter <- center transformNew.Rotation <- rotation this.SetTransformByRefWithoutEvent (&transformNew, world) else - if v3Neq transformOld.Position center || - quatNeq transformOld.Rotation rotation then + if transformOld.Position <> center || + transformOld.Rotation <> rotation then transformNew.Position <- center transformNew.Rotation <- rotation this.SetTransformByRefWithoutEvent (&transformNew, world) diff --git a/Nu/Nu/World/WorldFacets.fs b/Nu/Nu/World/WorldFacets.fs index 2cd6835eee..05a16bec43 100644 --- a/Nu/Nu/World/WorldFacets.fs +++ b/Nu/Nu/World/WorldFacets.fs @@ -276,7 +276,7 @@ type BasicStaticSpriteEmitterFacet () = static let handleEmitterImageChange evt world = let emitterImage = evt.Data.Value :?> Image AssetTag - mapEmitter (fun emitter -> if assetNeq emitter.Image emitterImage then { emitter with Image = emitterImage } else emitter) evt.Subscriber world + mapEmitter (fun emitter -> if emitter.Image <> emitterImage then { emitter with Image = emitterImage } else emitter) evt.Subscriber world Cascade static let handleEmitterLifeTimeOptChange evt world = @@ -323,7 +323,7 @@ type BasicStaticSpriteEmitterFacet () = | Some (:? Particles.BasicStaticSpriteEmitter as emitter) -> let position = entity.GetPosition world let emitter = - if v3Neq emitter.Body.Position position + if emitter.Body.Position <> position then { emitter with Body = { emitter.Body with Position = position }} else emitter { particleSystem with Emitters = Map.add typeof.Name (emitter :> Particles.Emitter) particleSystem.Emitters } @@ -339,7 +339,7 @@ type BasicStaticSpriteEmitterFacet () = | Some (:? Particles.BasicStaticSpriteEmitter as emitter) -> let angles = entity.GetAngles world let emitter = - if v3Neq emitter.Body.Angles angles + if emitter.Body.Angles <> angles then { emitter with Body = { emitter.Body with Angles = angles }} else emitter { particleSystem with Emitters = Map.add typeof.Name (emitter :> Particles.Emitter) particleSystem.Emitters } @@ -3024,7 +3024,7 @@ type BasicStaticBillboardEmitterFacet () = | Some (:? Particles.BasicStaticBillboardEmitter as emitter) -> let position = entity.GetPosition world let emitter = - if v3Neq emitter.Body.Position position + if emitter.Body.Position <> position then { emitter with Body = { emitter.Body with Position = position }} else emitter { particleSystem with Emitters = Map.add typeof.Name (emitter :> Particles.Emitter) particleSystem.Emitters } @@ -3040,7 +3040,7 @@ type BasicStaticBillboardEmitterFacet () = | Some (:? Particles.BasicStaticBillboardEmitter as emitter) -> let angles = entity.GetAngles world let emitter = - if v3Neq emitter.Body.Angles angles + if emitter.Body.Angles <> angles then { emitter with Body = { emitter.Body with Angles = angles }} else emitter { particleSystem with Emitters = Map.add typeof.Name (emitter :> Particles.Emitter) particleSystem.Emitters } diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index 0fb4bb013e..5fc02393bb 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -277,8 +277,8 @@ module WorldImGui = if ImGui.BeginCombo (name, caseName) then for case' in cases do let caseName' = case'.Name - if ImGui.Selectable (caseName', strEq caseName' caseName) then - if strNeq caseName caseName' then + if ImGui.Selectable (caseName', (caseName' = caseName)) then + if caseName <> caseName' then caseNameEdited <- true caseName <- caseName' ImGui.EndCombo () @@ -742,8 +742,8 @@ module WorldImGui = let mutable animationNameEdited = false if ImGui.BeginCombo (name, animationName) then for animationName' in animationNames do - if String.notEmpty animationName' && ImGui.Selectable (animationName', strEq animationName' animationName) then - if strNeq animationName animationName' then + if String.notEmpty animationName' && ImGui.Selectable (animationName', (animationName' = animationName)) then + if animationName <> animationName' then animationName <- animationName' animationNameEdited <- true ImGui.EndCombo () @@ -1105,8 +1105,8 @@ module WorldImGui = if ImGui.BeginCombo ("ParitionType", partitionTypeStr, ImGuiComboFlags.HeightLarge) then let partitionTypeStrs = Array.map (fun (ptv : RcPartitionType) -> ptv.Name) RcPartitionType.Values for partitionTypeStr' in partitionTypeStrs do - if ImGui.Selectable (partitionTypeStr', strEq partitionTypeStr' partitionTypeStr) then - if strNeq partitionTypeStr partitionTypeStr' then + if ImGui.Selectable (partitionTypeStr', (partitionTypeStr' = partitionTypeStr)) then + if partitionTypeStr <> partitionTypeStr' then partitionTypeStr <- partitionTypeStr' nav3dConfigEdited <- true ImGui.EndCombo () diff --git a/Nu/Nu/World/WorldModule2.fs b/Nu/Nu/World/WorldModule2.fs index 537b387026..4c38826f24 100644 --- a/Nu/Nu/World/WorldModule2.fs +++ b/Nu/Nu/World/WorldModule2.fs @@ -162,7 +162,7 @@ module WorldModule2 = match (selectedScreen.GetIncoming world).SongOpt with | Some playSong -> match World.getSongOpt world with - | Some song when assetEq song.Song playSong.Song -> () // do nothing when song is the same + | Some song when song.Song = playSong.Song -> () // do nothing when song is the same | _ -> World.playSong playSong.FadeInTime playSong.FadeOutTime GameTime.zero playSong.RepeatLimitOpt playSong.Volume playSong.Song world // play song when song is different | None -> () if world.Alive then @@ -177,7 +177,7 @@ module WorldModule2 = match (selectedScreen.GetIncoming world).SongOpt with | Some playSong -> match World.getSongOpt world with - | Some song when assetEq song.Song playSong.Song -> () // do nothing when song is the same + | Some song when song.Song = playSong.Song -> () // do nothing when song is the same | _ -> World.playSong playSong.FadeInTime playSong.FadeOutTime GameTime.zero playSong.RepeatLimitOpt playSong.Volume playSong.Song world // play song when song is different | None -> () match selectedScreen.GetSlideOpt world with @@ -242,7 +242,7 @@ module WorldModule2 = match destinationOpt with | Some destination -> match (incoming.SongOpt, (destination.GetIncoming world).SongOpt) with - | (Some song, Some song2) when assetEq song.Song song2.Song -> () // do nothing when song is the same + | (Some song, Some song2) when song.Song = song2.Song -> () // do nothing when song is the same | (None, None) -> () // do nothing when neither plays a song (allowing manual control) | (_, _) -> World.fadeOutSong playSong.FadeOutTime world // fade out when song is different | None -> @@ -312,7 +312,7 @@ module WorldModule2 = current.FadeOutTime <> song.FadeOutTime || current.StartTime <> song.StartTime || current.RepeatLimitOpt <> song.RepeatLimitOpt || - assetNeq current.Song song.Song then + current.Song <> song.Song then World.playSong song.FadeInTime song.FadeOutTime song.StartTime song.RepeatLimitOpt song.Volume song.Song world elif current.Volume <> song.Volume then World.setSongVolume song.Volume world diff --git a/Nu/Nu/World/WorldModuleEntity.fs b/Nu/Nu/World/WorldModuleEntity.fs index 6396880a4b..8ebad5701f 100644 --- a/Nu/Nu/World/WorldModuleEntity.fs +++ b/Nu/Nu/World/WorldModuleEntity.fs @@ -193,11 +193,11 @@ module WorldModuleEntity = static member internal publishTransformEvents (transformOld : Transform byref, transformNew : Transform byref, is2d, publishChangeEvents, entity : Entity, world) = if publishChangeEvents then - let positionChanged = v3Neq transformNew.Position transformOld.Position - let rotationChanged = quatNeq transformNew.Rotation transformOld.Rotation + let positionChanged = transformNew.Position <> transformOld.Position + let rotationChanged = transformNew.Rotation <> transformOld.Rotation let scaleChanged = v3NeqApprox transformNew.Scale transformOld.Scale 0.0001f // NOTE: just guessing at epsilon... - let offsetChanged = v3Neq transformNew.Offset transformOld.Offset - let sizeChanged = v3Neq transformNew.Size transformOld.Size + let offsetChanged = transformNew.Offset <> transformOld.Offset + let sizeChanged = transformNew.Size <> transformOld.Size let elevationChanged = transformNew.Elevation <> transformOld.Elevation let overflowChanged = transformNew.Overflow <> transformOld.Overflow World.publishEntityChange (nameof Transform) () () publishChangeEvents entity world // OPTIMIZATION: eliding data for computed change events for speed. @@ -860,7 +860,7 @@ module WorldModuleEntity = static member internal setEntityPresence (value : Presence) (entity : Entity) world = let entityState = World.getEntityState entity world let previous = entityState.Presence - if presenceNeq value previous then + if value <> previous then let visibleInViewOld = entityState.VisibleInView let staticInPlayOld = entityState.StaticInPlay let lightProbeOld = entityState.LightProbe @@ -970,7 +970,7 @@ module WorldModuleEntity = static member internal setEntityPosition value entity world = let entityState = World.getEntityState entity world - if v3Neq value entityState.Position then + if value <> entityState.Position then let mutable transform = entityState.Transform transform.Position <- value if entityState.Optimized world.Imperative @@ -983,7 +983,7 @@ module WorldModuleEntity = // ensure value changed let entityState = World.getEntityState entity world - if v3Neq value entityState.PositionLocal then + if value <> entityState.PositionLocal then // OPTIMIZATION: do updates and propagation in-place as much as possible. if entityState.Optimized world.Imperative then @@ -1040,7 +1040,7 @@ module WorldModuleEntity = static member internal setEntityRotation value entity world = let entityState = World.getEntityState entity world - if quatNeq value entityState.Rotation then + if value <> entityState.Rotation then let mutable transform = entityState.Transform transform.Rotation <- value if entityState.Optimized world.Imperative @@ -1053,7 +1053,7 @@ module WorldModuleEntity = // ensure value changed let entityState = World.getEntityState entity world - if quatNeq value entityState.RotationLocal then + if value <> entityState.RotationLocal then // OPTIMIZATION: do updates and propagation in-place as much as possible. let anglesLocal = value.RollPitchYaw @@ -1076,7 +1076,7 @@ module WorldModuleEntity = let previous = entityState.RotationLocal let previousAnglesLocal = entityState.AnglesLocal let previousDegreesLocal = entityState.DegreesLocal - if quatNeq value previous then + if value <> previous then let entityState = if world.Imperative then entityState.RotationLocal <- value @@ -1111,7 +1111,7 @@ module WorldModuleEntity = static member internal setEntityScale value entity world = let entityState = World.getEntityState entity world - if v3Neq value entityState.Scale then + if value <> entityState.Scale then let mutable transform = entityState.Transform transform.Scale <- value if entityState.Optimized world.Imperative @@ -1124,7 +1124,7 @@ module WorldModuleEntity = // ensure value changed let entityState = World.getEntityState entity world - if v3Neq value entityState.ScaleLocal then + if value <> entityState.ScaleLocal then // OPTIMIZATION: do updates and propagation in-place as much as possible. if entityState.Optimized world.Imperative then @@ -1143,7 +1143,7 @@ module WorldModuleEntity = // update ScaleLocal property let previous = entityState.ScaleLocal - if v3Neq value previous then + if value <> previous then let entityState = if world.Imperative then entityState.ScaleLocal <- value @@ -1174,7 +1174,7 @@ module WorldModuleEntity = static member internal setEntityOffset value entity world = let entityState = World.getEntityState entity world - if v3Neq value entityState.Offset then + if value <> entityState.Offset then let mutable transform = entityState.Transform transform.Offset <- value if entityState.Optimized world.Imperative @@ -1185,7 +1185,7 @@ module WorldModuleEntity = static member internal setEntitySize value entity world = let entityState = World.getEntityState entity world - if v3Neq value entityState.Size then + if value <> entityState.Size then let centerPrevious = entityState.PerimeterCenterLocal let bottomPrevious = entityState.PerimeterBottomLocal let bottomLeftPrevious = entityState.PerimeterBottomLeftLocal @@ -1208,7 +1208,7 @@ module WorldModuleEntity = static member internal setEntityAngles value entity world = let entityState = World.getEntityState entity world - if v3Neq value entityState.Angles then + if value <> entityState.Angles then let mutable transform = entityState.Transform transform.Angles <- value if entityState.Optimized world.Imperative @@ -1221,7 +1221,7 @@ module WorldModuleEntity = // ensure value changed let entityState = World.getEntityState entity world - if v3Neq value entityState.AnglesLocal then + if value <> entityState.AnglesLocal then // OPTIMIZATION: do updates and propagation in-place as much as possible. let rotationLocal = value.RollPitchYaw @@ -1512,7 +1512,7 @@ module WorldModuleEntity = static member internal setEntityPerimeterUnscaled value entity world = let entityState = World.getEntityState entity world - if box3Neq value entityState.PerimeterUnscaled then + if value <> entityState.PerimeterUnscaled then let mutable transform = entityState.Transform transform.PerimeterUnscaled <- value if entityState.Optimized world.Imperative @@ -1526,7 +1526,7 @@ module WorldModuleEntity = static member internal setEntityPerimeter value entity world = let entityState = World.getEntityState entity world - if box3Neq value entityState.Perimeter then + if value <> entityState.Perimeter then let mutable transform = entityState.Transform transform.Perimeter <- value if entityState.Optimized world.Imperative @@ -2665,9 +2665,9 @@ module WorldModuleEntity = staticInPlayNew <> staticInPlayOld || lightProbeNew <> lightProbeOld || lightNew <> lightOld || - presenceNeq presenceNew presenceOld || - presenceNeq presenceInPlayNew presenceInPlayOld || - box3Neq boundsOld boundsNew then + presenceNew <> presenceOld || + presenceInPlayNew <> presenceInPlayOld || + boundsOld <> boundsNew then // update entity in entity tree if entityState.Is2d then diff --git a/Nu/Nu/World/WorldModuleGame.fs b/Nu/Nu/World/WorldModuleGame.fs index 570b47b2ca..9a5f475f9b 100644 --- a/Nu/Nu/World/WorldModuleGame.fs +++ b/Nu/Nu/World/WorldModuleGame.fs @@ -217,7 +217,7 @@ module WorldModuleGame = static member internal setGameEye2dCenter value game world = let gameState = World.getGameState game world let previous = gameState.Eye2dCenter - if v2Neq previous value then + if previous <> value then World.setGameState { gameState with Eye2dCenter = value } game world World.publishGameChange (nameof gameState.Eye2dCenter) previous value game world true @@ -237,7 +237,7 @@ module WorldModuleGame = static member internal setGameEye2dSize value game world = let gameState = World.getGameState game world let previous = gameState.Eye2dSize - if v2Neq previous value then + if previous <> value then World.setGameState { gameState with Eye2dSize = value } game world World.publishGameChange (nameof gameState.Eye2dSize) previous value game world true @@ -318,7 +318,7 @@ module WorldModuleGame = static member internal setGameEye3dCenter (value : Vector3) game world = let gameState = World.getGameState game world let previous = gameState.Eye3dCenter - if v3Neq previous value then + if previous <> value then let viewportInterior = Viewport.makeInterior () let viewportExterior = Viewport.makeExterior () let viewportImposter = Viewport.makeImposter () @@ -347,7 +347,7 @@ module WorldModuleGame = static member internal setGameEye3dRotation value game world = let gameState = World.getGameState game world let previous = gameState.Eye3dRotation - if quatNeq previous value then + if previous <> value then let viewportInterior = Viewport.makeInterior () let viewportExterior = Viewport.makeExterior () let viewportImposter = Viewport.makeImposter () From 3e1474a8c3dfed6718894eb2f10063fcde5990a0 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 13 Nov 2025 09:12:57 -0500 Subject: [PATCH 35/52] Cleaned up no longer applicable ignore usage. --- Nu/Nu/World/WorldModuleEntity.fs | 4 ++-- Nu/Nu/World/WorldModuleGame.fs | 4 ++-- Nu/Nu/World/WorldModuleGroup.fs | 4 ++-- Nu/Nu/World/WorldModuleScreen.fs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Nu/Nu/World/WorldModuleEntity.fs b/Nu/Nu/World/WorldModuleEntity.fs index 8ebad5701f..ac25859fe2 100644 --- a/Nu/Nu/World/WorldModuleEntity.fs +++ b/Nu/Nu/World/WorldModuleEntity.fs @@ -1847,7 +1847,7 @@ module WorldModuleEntity = | Some computedSet -> let previous = cp.ComputedGet (box entity) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue entity world |> ignore // TODO: P0: same as the others. + computedSet property.PropertyValue entity world struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -1931,7 +1931,7 @@ module WorldModuleEntity = previous <- cp.ComputedGet (box entity) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue entity world |> ignore // TODO: P0: same as the others. + computedSet propertyOld.PropertyValue entity world | None -> () | _ -> previous <- propertyOld.PropertyValue diff --git a/Nu/Nu/World/WorldModuleGame.fs b/Nu/Nu/World/WorldModuleGame.fs index 9a5f475f9b..19af0828ea 100644 --- a/Nu/Nu/World/WorldModuleGame.fs +++ b/Nu/Nu/World/WorldModuleGame.fs @@ -705,7 +705,7 @@ module WorldModuleGame = | Some computedSet -> let previous = cp.ComputedGet (box game) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue game world |> ignore + computedSet property.PropertyValue game world struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -760,7 +760,7 @@ module WorldModuleGame = previous <- cp.ComputedGet (box game) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue game world |> ignore + computedSet propertyOld.PropertyValue game world | None -> () | _ -> previous <- propertyOld.PropertyValue diff --git a/Nu/Nu/World/WorldModuleGroup.fs b/Nu/Nu/World/WorldModuleGroup.fs index 39c774c0a0..fd650e4262 100644 --- a/Nu/Nu/World/WorldModuleGroup.fs +++ b/Nu/Nu/World/WorldModuleGroup.fs @@ -289,7 +289,7 @@ module WorldModuleGroup = | Some computedSet -> let previous = cp.ComputedGet (box group) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue group world |> ignore + computedSet property.PropertyValue group world struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -344,7 +344,7 @@ module WorldModuleGroup = previous <- cp.ComputedGet (box group) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue group world |> ignore + computedSet propertyOld.PropertyValue group world | None -> () | _ -> previous <- propertyOld.PropertyValue diff --git a/Nu/Nu/World/WorldModuleScreen.fs b/Nu/Nu/World/WorldModuleScreen.fs index 57a0b5e75b..f4cc812c07 100644 --- a/Nu/Nu/World/WorldModuleScreen.fs +++ b/Nu/Nu/World/WorldModuleScreen.fs @@ -336,7 +336,7 @@ module WorldModuleScreen = | Some computedSet -> let previous = cp.ComputedGet (box screen) (box world) if property.PropertyValue =/= previous then - computedSet property.PropertyValue screen world |> ignore + computedSet property.PropertyValue screen world struct (true, true, previous) else struct (true, false, previous) | None -> struct (false, false, Unchecked.defaultof<_>) @@ -391,7 +391,7 @@ module WorldModuleScreen = previous <- cp.ComputedGet (box screen) (box world) if value =/= previous then changed <- true - computedSet propertyOld.PropertyValue screen world |> ignore + computedSet propertyOld.PropertyValue screen world | None -> () | _ -> previous <- propertyOld.PropertyValue From f4d9fb326c8e93b2b6edb7ff5424264af30637f9 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 13 Nov 2025 21:51:38 -0500 Subject: [PATCH 36/52] Removed cone angle caps on spot lights. --- Nu/Nu/Render/Renderer3d.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nu/Nu/Render/Renderer3d.fs b/Nu/Nu/Render/Renderer3d.fs index d9a2dc0f32..72bfac9872 100644 --- a/Nu/Nu/Render/Renderer3d.fs +++ b/Nu/Nu/Render/Renderer3d.fs @@ -2444,7 +2444,7 @@ type [] GlRenderer3d = | NormalPass -> Presence.intersects3d (ValueSome frustumInterior) frustumExterior frustumImposter (ValueSome lightBox) false true presence lightBounds | _ -> false if unculled then - let coneOuter = match light.LightType with SpotLight (_, coneOuter) -> min coneOuter MathF.PI_MINUS_EPSILON | _ -> MathF.TWO_PI + let coneOuter = match light.LightType with SpotLight (_, coneOuter) -> min coneOuter MathF.TWO_PI | _ -> MathF.TWO_PI let coneInner = match light.LightType with SpotLight (coneInner, _) -> min coneInner coneOuter | _ -> MathF.TWO_PI let light = { SortableLightId = 0UL @@ -2770,7 +2770,7 @@ type [] GlRenderer3d = | RenderLight3d rl -> let direction = rl.Rotation.Down let renderTasks = GlRenderer3d.getRenderTasks rl.RenderPass renderer - let coneOuter = match rl.LightType with SpotLight (_, coneOuter) -> min coneOuter MathF.PI_MINUS_EPSILON | _ -> MathF.TWO_PI + let coneOuter = match rl.LightType with SpotLight (_, coneOuter) -> min coneOuter MathF.TWO_PI | _ -> MathF.TWO_PI let coneInner = match rl.LightType with SpotLight (coneInner, _) -> min coneInner coneOuter | _ -> MathF.TWO_PI let light = { SortableLightId = rl.LightId From 9f3f946f04542649b86fc4c648d620803c810bb5 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 13 Nov 2025 22:03:25 -0500 Subject: [PATCH 37/52] Shader code clean-up. --- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 6 +++--- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 6 +++--- 51 files changed, 153 insertions(+), 153 deletions(-) diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 90463f4df8..1a3b687eaf 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -778,11 +778,11 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; float intensity = 0.0; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl index b534beef6f..a3ed7f6db4 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -862,10 +862,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl index b235d2acbd..3631a3106c 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -843,10 +843,10 @@ void main() vec3 lightOrigin = lightOrigins[i]; float lightCutoff = lightCutoffs[i]; int lightType = lightTypes[i]; - bool lightDirectional = lightType == 2; - bool lightCascaded = lightType == 3; + bool lightPoint = lightType == 0; + bool lightSpot = lightType == 1; vec3 l, h, radiance; - if (!lightDirectional && !lightCascaded) + if (lightPoint || lightSpot) { vec3 d = lightOrigin - position.xyz; l = normalize(d); From 5e80420f0e210b8c72c3440282c5e9d918bbf7ec Mon Sep 17 00:00:00 2001 From: bryanedds Date: Thu, 13 Nov 2025 22:44:21 -0500 Subject: [PATCH 38/52] .NET 10 compile fixes. --- Nu/Nu/Render/RendererImGui.fs | 2 +- Nu/Nu/World/WorldContent.fs | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Nu/Nu/Render/RendererImGui.fs b/Nu/Nu/Render/RendererImGui.fs index f008d141ca..15de3ef17f 100644 --- a/Nu/Nu/Render/RendererImGui.fs +++ b/Nu/Nu/Render/RendererImGui.fs @@ -193,7 +193,7 @@ type GlRendererImGui let now = DateTimeOffset.Now let assetTags = Array.ofSeq assetTextureRequests.Keys // eager copy to allow modification during enumeration let mutable assetTagsEnr = (seq assetTags).GetEnumerator () - while assetTagsEnr.MoveNext () && DateTimeOffset.Now - now <= TimeSpan.FromMilliseconds 4 do + while assetTagsEnr.MoveNext () && DateTimeOffset.Now - now <= TimeSpan.FromMilliseconds 4.0 do let assetTag = assetTagsEnr.Current if not (assetTextureOpts.ContainsKey assetTag) then match Metadata.tryGetFilePath assetTag with diff --git a/Nu/Nu/World/WorldContent.fs b/Nu/Nu/World/WorldContent.fs index 84d7e848fc..ee1e0760b3 100644 --- a/Nu/Nu/World/WorldContent.fs +++ b/Nu/Nu/World/WorldContent.fs @@ -162,8 +162,9 @@ module Content = let childrenPotentiallyAltered = OrderedDictionary HashIdentity.Structural let childrenAdded = List () for childEntry in childContents do - match childContentsOld.TryGetValue childEntry.Key with - | (true, childContentOld) when childEntry.Value.DispatcherNameOpt = childContentOld.DispatcherNameOpt -> + let mutable childContentOld = Unchecked.defaultof<_> + if childContentsOld.TryGetValue (childEntry.Key, &childContentOld) && + childEntry.Value.DispatcherNameOpt = childContentOld.DispatcherNameOpt then let childSimulant = // OPTIMIZATION: attempt to get child simulant from old content rather than deriving it, and store it for future use. if isNull (childContentOld.SimulantCachedOpt :> obj) then let derived = World.deriveFromNames (Array.add childEntry.Key simulant.SimulantAddress.Names) :?> 'child @@ -174,15 +175,17 @@ module Content = childEntry.Value.SimulantCachedOpt <- found found childrenPotentiallyAltered.Add (childSimulant, childEntry.Value) - | (_, _) -> + else let childSimulant = World.deriveFromNames (Array.add childEntry.Key simulant.SimulantAddress.Names) :?> 'child childEntry.Value.SimulantCachedOpt <- childSimulant childrenAdded.Add (childSimulant, childEntry.Value) let childrenRemoved = List<'child> () for childEntryOld in childContentsOld do - match childContents.TryGetValue childEntryOld.Key with - | (true, childContentOld) when childEntryOld.Value.DispatcherNameOpt = childContentOld.DispatcherNameOpt -> () - | (_, _) -> + let mutable childContentOld = Unchecked.defaultof<_> + if childContents.TryGetValue (childEntryOld.Key, &childContentOld) && + childEntryOld.Value.DispatcherNameOpt = childContentOld.DispatcherNameOpt then + () // nothing to do + else let childSimulant = childEntryOld.Value.SimulantCachedOpt :?> 'child // OPTIMIZATION: because of above optimization, should be guaranteed to exist. childrenRemoved.Add childSimulant childrenPotentiallyAltered.Remove childSimulant |> ignore From e6a119493e7f6ca899986195355911fa537b93e5 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Fri, 14 Nov 2025 01:08:17 -0500 Subject: [PATCH 39/52] Added creation controls to assets. --- Nu/Nu.Gaia/Gaia.fs | 108 ++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 31 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index d6789d806c..e7354c2516 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -884,9 +884,9 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= entity.ResetProbeBounds world | Some _ | None -> () - let private createEntity atMouse inHierarchy world = + let private createEntity atMouse inHierarchy dispatcherNameOverride parentOverride world = snapshot CreateEntity world - let dispatcherName = NewEntityDispatcherName + let dispatcherName = Option.defaultValue NewEntityDispatcherName dispatcherNameOverride let overlayNameDescriptor = match NewEntityOverlayName with | "(Default Overlay)" -> DefaultOverlay @@ -902,7 +902,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= then Array.add name entity.Surnames else [|name|] else - match NewEntityParentOpt with + match Option.orElse NewEntityParentOpt parentOverride with | Some newEntityParent when newEntityParent.GetExists world -> Array.add name newEntityParent.Surnames | Some _ | None -> [|name|] | None -> [|name|] @@ -911,6 +911,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= selectEntityOpt (Some entity) world ImGui.SetWindowFocus "Viewport" ShowSelectedEntity <- true + entity let private trySaveSelectedEntity filePath world = match SelectedEntityOpt with @@ -1673,7 +1674,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= elif ImGui.IsKeyPressed ImGuiKey.X && ImGui.IsCtrlDown () then tryCutSelectedEntity world |> ignore elif ImGui.IsKeyPressed ImGuiKey.C && ImGui.IsCtrlDown () then tryCopySelectedEntity world |> ignore elif ImGui.IsKeyPressed ImGuiKey.V && ImGui.IsCtrlDown () then tryPaste PasteAtLook (Option.map cast NewEntityParentOpt) world |> ignore - elif ImGui.IsKeyPressed ImGuiKey.Enter && ImGui.IsCtrlDown () then createEntity false false world + elif ImGui.IsKeyPressed ImGuiKey.Enter && ImGui.IsCtrlDown () then createEntity false false None None world |> ignore elif ImGui.IsKeyPressed ImGuiKey.Delete then tryDeleteSelectedEntity world |> ignore elif ImGui.IsKeyPressed ImGuiKey.Escape then if not (String.IsNullOrWhiteSpace PropagationSourcesSearchStr) then @@ -1725,9 +1726,9 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if ImGui.BeginPopupContextItem popupContextItemTitle then if ImGui.IsMouseReleased ImGuiMouseButton.Right then openPopupContextItemWhenUnselected <- true selectEntityOpt (Some entity) world - if ImGui.MenuItem "Create Entity" then createEntity false true world + if ImGui.MenuItem "Create Entity" then createEntity false true None None world |> ignore if SelectedEntityOpt.IsSome && ImGui.MenuItem "Create Entity at Local Origin" then - createEntity true true world + createEntity true true None None world |> ignore tryMoveSelectedEntityToOrigin true world |> ignore if ImGui.MenuItem "Delete Entity" then tryDeleteSelectedEntity world |> ignore ImGui.Separator () @@ -2459,7 +2460,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= // entity menu if ImGui.BeginMenu "Entity" then - if ImGui.MenuItem ("Create Entity", "Ctrl+Enter") then createEntity false false world + if ImGui.MenuItem ("Create Entity", "Ctrl+Enter") then createEntity false false None None world |> ignore if ImGui.MenuItem ("Delete Entity", "Delete") then tryDeleteSelectedEntity world |> ignore ImGui.Separator () if ImGui.MenuItem ("Cut Entity", "Ctrl+X") then tryCutSelectedEntity world |> ignore @@ -2494,7 +2495,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.EndMenuBar () // tool bar - if ImGui.Button "Create" then createEntity false false world + if ImGui.Button "Create" then createEntity false false None None world |> ignore ImGui.SameLine () ImGui.SetNextItemWidth 200.0f if ImGui.BeginCombo ("##newEntityDispatcherName", NewEntityDispatcherName, ImGuiComboFlags.HeightRegular) then @@ -2863,13 +2864,8 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.PushID ("##asChild" + scstringMemo entity) if ImGui.SmallButton "as Child" then snapshot DuplicateEntity world - let parent = - SelectedEntityOpt - |> Option.map cast - |> Option.orElse (Option.map cast NewEntityParentOpt) - |> Option.defaultValue entity.Group let positionSnapEir = if Snaps2dSelected then Left (a__ Snaps2d) else Right (a__ Snaps3d) - let duplicate = World.pasteEntity NewEntityDistance RightClickPosition positionSnapEir PasteAtLook entity parent world + let duplicate = World.pasteEntity NewEntityDistance RightClickPosition positionSnapEir PasteAtLook entity selectedEntity world selectEntityOpt (Some duplicate) world ImGui.PopID () if ImGui.IsItemHovered ImGuiHoveredFlags.DelayNormal && ImGui.BeginTooltip () then @@ -2879,13 +2875,8 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.PushID ("##atLocalOrigin" + scstringMemo entity) if ImGui.SmallButton "at Local Origin" then snapshot DuplicateEntity world - let parent = - SelectedEntityOpt - |> Option.map cast - |> Option.orElse (Option.map cast NewEntityParentOpt) - |> Option.defaultValue entity.Group let positionSnapEir = if Snaps2dSelected then Left (a__ Snaps2d) else Right (a__ Snaps3d) - let duplicate = World.pasteEntity NewEntityDistance RightClickPosition positionSnapEir PasteAtLook entity parent world + let duplicate = World.pasteEntity NewEntityDistance RightClickPosition positionSnapEir PasteAtLook entity selectedEntity world duplicate.SetPositionLocal v3Zero world selectEntityOpt (Some duplicate) world ImGui.PopID () @@ -3386,7 +3377,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let searchDeactivated = searchActivePrevious && not searchActiveCurrent ImGui.BeginChild "Container" |> ignore for packageEntry in Metadata.getMetadataPackagesLoaded () |> Array.sortWith (fun a b -> String.Compare (a.Key, b.Key, true)) do - let flags = ImGuiTreeNodeFlags.SpanAvailWidth ||| ImGuiTreeNodeFlags.OpenOnArrow + let flags = ImGuiTreeNodeFlags.OpenOnArrow if searchActiveCurrent then ImGui.SetNextItemOpen true if searchDeactivated then ImGui.SetNextItemOpen false if ImGui.TreeNodeEx (packageEntry.Key, flags) then @@ -3399,14 +3390,14 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let assetImageSize = v2Dup (ImGui.GetFontSize () + 3.0f) let image = match __c assetEntry.Value with - | RawMetadata -> asset Assets.Default.PackageName "RawIcon" + | RawMetadata -> Assets.Default.RawIconIcon | TextureMetadata _ -> asset packageName assetName - | TileMapMetadata _ -> asset Assets.Default.PackageName "TileMapIcon" - | SpineSkeletonMetadata _ -> asset Assets.Default.PackageName "SpineSkeletonIcon" - | StaticModelMetadata _ -> asset Assets.Default.PackageName "StaticModelIcon" - | AnimatedModelMetadata _ -> asset Assets.Default.PackageName "AnimatedModelIcon" - | SoundMetadata -> asset Assets.Default.PackageName "SoundIcon" - | SongMetadata -> asset Assets.Default.PackageName "SongIcon" + | TileMapMetadata _ -> Assets.Default.TileMapIcon + | SpineSkeletonMetadata _ -> Assets.Default.SpineSkeletonIcon + | StaticModelMetadata _ -> Assets.Default.StaticModelIcon + | AnimatedModelMetadata _ -> Assets.Default.AnimatedModelIcon + | SoundMetadata -> Assets.Default.SoundIcon + | SongMetadata -> Assets.Default.SongIcon match World.imGuiTryGetTextureId image world with | ValueSome textureId -> ImGui.Image (nativeint textureId, assetImageSize) @@ -3430,6 +3421,61 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.Text assetTagStr ImGui.SetDragDropPayload ("Asset", IntPtr.Zero, 0u) |> ignore ImGui.EndDragDropSource () + let creatorOpt = + match __c assetEntry.Value with + | RawMetadata -> None + | TextureMetadata _ -> + (fun parentOpt world -> + let entity = createEntity false false (Some (nameof StaticSpriteDispatcher)) parentOpt world + entity.SetStaticImage (asset packageName assetName) world + entity.AutoBounds world + entity) |> Some + | TileMapMetadata _ -> + (fun parentOpt world -> + let entity = createEntity false false (Some (nameof TileMapDispatcher)) parentOpt world + entity.SetTileMap (asset packageName assetName) world + entity.AutoBounds world + entity) |> Some + | SpineSkeletonMetadata _ -> + (fun parentOpt world -> + let entity = createEntity false false (Some (nameof SpineSkeletonDispatcher)) parentOpt world + entity.SetSpineSkeleton (asset packageName assetName) world + entity.AutoBounds world + entity) |> Some + | StaticModelMetadata _ -> + (fun parentOpt world -> + let entity = createEntity false false (Some (nameof StaticModelDispatcher)) parentOpt world + entity.SetStaticModel (asset packageName assetName) world + entity.AutoBounds world + entity) |> Some + | AnimatedModelMetadata _ -> + (fun parentOpt world -> + let entity = createEntity false false (Some (nameof AnimatedModelDispatcher)) parentOpt world + entity.SetAnimatedModel (asset packageName assetName) world + entity.AutoBounds world + entity) |> Some + | SoundMetadata -> + ImGui.SameLine () + if ImGui.SmallButton "Play" then + World.playSound 0.0f 0.0f 1.0f (asset packageName assetName) world + None + | SongMetadata -> None + match creatorOpt with + | Some creator -> + ImGui.SameLine () + if ImGui.SmallButton "Create" then + creator None world |> ignore + match SelectedEntityOpt with + | Some selectedEntity -> + ImGui.SameLine () + if ImGui.SmallButton "as Child" then + creator (Some selectedEntity) world |> ignore + ImGui.SameLine () + if ImGui.SmallButton "at Local Origin" then + let entity = creator (Some selectedEntity) world + entity.SetPositionLocal v3Zero world + | None -> () + | None -> () ImGui.TreePop () ImGui.TreePop () ImGui.EndChild () @@ -3869,7 +3915,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.SetNextWindowSize (v2 290.0f -1.0f) if ImGui.Begin ("Context Menu", ImGuiWindowFlags.NoTitleBar ||| ImGuiWindowFlags.NoNav) then if ImGui.Button "Create" then - createEntity true false world + createEntity true false None None world |> ignore ShowEntityContextMenu <- false ImGui.SameLine () ImGui.SetNextItemWidth -1.0f @@ -3884,11 +3930,11 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.EndCombo () if SelectedEntityOpt.IsSome then if ImGui.Button "Create as Child" then - createEntity true true world + createEntity true true None None world |> ignore ShowEntityContextMenu <- false ImGui.SameLine () if ImGui.Button "at Local Origin" then - createEntity true true world + createEntity true true None None world |> ignore tryMoveSelectedEntityToOrigin true world |> ignore ShowEntityContextMenu <- false if ImGui.Button "Delete" then From 39d354cd628d71e8dc6e2204e79b0e987b6c0046 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Fri, 14 Nov 2025 13:19:18 -0500 Subject: [PATCH 40/52] Bit of a hack to fix special entity creation functions. --- Nu/Nu.Gaia/Gaia.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index e7354c2516..38d7cd3716 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -878,6 +878,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= else entity.SetTransform entityTransform world if entity.Surnames.Length > 1 then if World.getEntityAllowedToMount entity world then + entity.SetMountOpt None world // NOTE: we need to unmount for the following line to have effect. entity.SetMountOptWithAdjustment (Some Address.parent) world match entity.TryGetProperty (nameof entity.ProbeBounds) world with | Some property when property.PropertyType = typeof -> From 5f2ef32e286b7086b1eb5d4ca266153d17305d79 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Fri, 14 Nov 2025 13:39:36 -0500 Subject: [PATCH 41/52] *BREAKING*: Added flag to SetMountOptWithAdjustment to make mount non-change behavior explicit. --- Nu/Nu.Gaia/Gaia.fs | 5 ++--- Nu/Nu/World/WorldEntity.fs | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 38d7cd3716..1165049097 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -878,8 +878,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= else entity.SetTransform entityTransform world if entity.Surnames.Length > 1 then if World.getEntityAllowedToMount entity world then - entity.SetMountOpt None world // NOTE: we need to unmount for the following line to have effect. - entity.SetMountOptWithAdjustment (Some Address.parent) world + entity.SetMountOptWithAdjustment true (Some Address.parent) world match entity.TryGetProperty (nameof entity.ProbeBounds) world with | Some property when property.PropertyType = typeof -> entity.ResetProbeBounds world @@ -2721,7 +2720,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let sourceEntity' = Nu.Entity (SelectedGroup.GroupAddress <-- Address.makeFromName sourceEntity.Name) if not (sourceEntity'.GetExists world) then if World.getEntityAllowedToMount sourceEntity world then - sourceEntity.SetMountOptWithAdjustment None world + sourceEntity.SetMountOptWithAdjustment false None world World.renameEntityImmediate sourceEntity sourceEntity' world if NewEntityParentOpt = Some sourceEntity then NewEntityParentOpt <- Some sourceEntity' selectEntityOpt (Some sourceEntity') world diff --git a/Nu/Nu/World/WorldEntity.fs b/Nu/Nu/World/WorldEntity.fs index a6e75a7848..ccae057e1f 100644 --- a/Nu/Nu/World/WorldEntity.fs +++ b/Nu/Nu/World/WorldEntity.fs @@ -423,10 +423,11 @@ module WorldEntityModule = member this.AutoBounds world = World.autoBoundsEntity this world /// Set an entity's mount while adjusting its mount properties such that they do not change. - member this.SetMountOptWithAdjustment (value : Entity Address option) world = + member this.SetMountOptWithAdjustment presumeChange (value : Entity Address option) world = match (Option.bind (flip tryResolve this) (this.GetMountOpt world), Option.bind (flip tryResolve this) value) with | (Some mountOld, Some mountNew) -> - if mountOld <> mountNew && mountOld.GetExists world && mountNew.GetExists world then + let adjustmentDesired = presumeChange || mountOld <> mountNew + if adjustmentDesired && mountOld.GetExists world && mountNew.GetExists world then let affineMatrixMount = World.getEntityAffineMatrix mountNew world let affineMatrixMounter = World.getEntityAffineMatrix this world let affineMatrixLocal = affineMatrixMounter * affineMatrixMount.Inverted @@ -608,8 +609,7 @@ module WorldEntityModule = if source.Parent <> destination.Parent && Option.isSome mountOpt && World.getEntityAllowedToMount destination world then - destination.SetMountOptWithAdjustment None world // NOTE: we have to set mount to none in order to convince the engine it's changing. - destination.SetMountOptWithAdjustment mountOpt world + destination.SetMountOptWithAdjustment true mountOpt world /// Rename an entity. static member renameEntity source destination world = @@ -903,7 +903,7 @@ module WorldEntityModule = if descendantSource.GetExists world && descendantSource.HasPropagationTargets world then World.setEntityPropagationSourceOpt (Some descendantSource) descendentEntity world |> ignore let mountOpt = match parent with :? Entity -> Some Address.parent | _ -> None - entity.SetMountOptWithAdjustment mountOpt world + entity.SetMountOptWithAdjustment false mountOpt world entity /// Paste an entity. From 81e5410a14b58f7e4210bbef52935d71d61e80b6 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Fri, 14 Nov 2025 14:49:06 -0500 Subject: [PATCH 42/52] Word choice fix. --- Nu/Nu/World/WorldEntity.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nu/Nu/World/WorldEntity.fs b/Nu/Nu/World/WorldEntity.fs index ccae057e1f..d27e13f444 100644 --- a/Nu/Nu/World/WorldEntity.fs +++ b/Nu/Nu/World/WorldEntity.fs @@ -423,10 +423,10 @@ module WorldEntityModule = member this.AutoBounds world = World.autoBoundsEntity this world /// Set an entity's mount while adjusting its mount properties such that they do not change. - member this.SetMountOptWithAdjustment presumeChange (value : Entity Address option) world = + member this.SetMountOptWithAdjustment assumeChange (value : Entity Address option) world = match (Option.bind (flip tryResolve this) (this.GetMountOpt world), Option.bind (flip tryResolve this) value) with | (Some mountOld, Some mountNew) -> - let adjustmentDesired = presumeChange || mountOld <> mountNew + let adjustmentDesired = assumeChange || mountOld <> mountNew if adjustmentDesired && mountOld.GetExists world && mountNew.GetExists world then let affineMatrixMount = World.getEntityAffineMatrix mountNew world let affineMatrixMounter = World.getEntityAffineMatrix this world From 55404220f71d117dbf9c112b4ce092488645e734 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Fri, 14 Nov 2025 22:41:48 -0500 Subject: [PATCH 43/52] Partial implementation of #383. Renamed OverlayNameDescriptor. --- Nu/Nu.Gaia/Gaia.fs | 197 ++++++++++++++++++++++-------------- Nu/Nu.Gaia/GaiaPrelude.fs | 2 +- Nu/Nu/World/WorldImGui.fs | 6 +- Nu/Nu/World/WorldImSim.fs | 2 +- Nu/Nu/World/WorldPrelude.fs | 4 +- Nu/Nu/World/WorldTypes.fs | 9 +- 6 files changed, 136 insertions(+), 84 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 1165049097..71d292679e 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -61,7 +61,7 @@ module Gaia = let mutable private PropertyEditorFocusRequested = false let mutable private EntityHierarchySearchRequested = false let mutable private AssetViewerSearchRequested = false - let mutable private DragDropPayloadOpt = None + let mutable private DragDropPayloadOpt = Option.None let mutable private DragEntityState = DragEntityInactive let mutable private DragEyeState = DragEyeInactive let mutable private SelectedScreen = Game / "Screen" // TODO: see if this is necessary or if we can just use World.getSelectedScreen. @@ -1454,6 +1454,68 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= (* Update Functions *) + let private updateEyeDrag world = + if canEditWithMouse world then + if ImGui.IsMouseClicked ImGuiMouseButton.Middle then + let mousePositionInset = World.getMousePosition2dInset world + let dragState = DragEye2dCenter (world.Eye2dCenter + mousePositionInset, mousePositionInset) + DragEyeState <- dragState + match DragEyeState with + | DragEye2dCenter (entityDragOffset, mousePositionInsetOrig) -> + let mousePositionInset = World.getMousePosition2dInset world + DesiredEye2dCenter <- (entityDragOffset - mousePositionInsetOrig) + -Constants.Gaia.EyeSpeed * (mousePositionInset - mousePositionInsetOrig) + DragEyeState <- DragEye2dCenter (entityDragOffset, mousePositionInsetOrig) + | DragEyeInactive -> () + if ImGui.IsMouseReleased ImGuiMouseButton.Middle then + match DragEyeState with + | DragEye2dCenter _ -> DragEyeState <- DragEyeInactive + | DragEyeInactive -> () + + let private updateEyeTravel world = + if canEditWithKeyboard world then + let delta = world.DateDelta + let seconds = single delta.TotalSeconds + let center = world.Eye3dCenter + let rotation = world.Eye3dRotation + let moveSpeed = + if ImGui.IsEnterDown () && ImGui.IsShiftDown () then 512.0f * seconds + elif ImGui.IsEnterDown () then 64.0f * seconds + elif ImGui.IsShiftDown () then 1.0f * seconds + else 8.0f * seconds + let turnSpeed = + if ImGui.IsShiftDown () && ImGui.IsEnterUp () then 1.5f * seconds + else 3.0f * seconds + if ImGui.IsKeyDown ImGuiKey.W && ImGui.IsCtrlUp () && ImGui.IsAltUp () then + DesiredEye3dCenter <- center + v3Forward.Transform rotation * moveSpeed + if ImGui.IsKeyDown ImGuiKey.S && ImGui.IsCtrlUp () && ImGui.IsAltUp () then + DesiredEye3dCenter <- center + v3Back.Transform rotation * moveSpeed + if ImGui.IsKeyDown ImGuiKey.A && ImGui.IsCtrlUp () && ImGui.IsAltUp () then + DesiredEye3dCenter <- center + v3Left.Transform rotation * moveSpeed + if ImGui.IsKeyDown ImGuiKey.D && ImGui.IsCtrlUp () && ImGui.IsAltUp () then + DesiredEye3dCenter <- center + v3Right.Transform rotation * moveSpeed + if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.UpArrow else ImGuiKey.E) && ImGui.IsCtrlUp () then + let rotation' = rotation * Quaternion.CreateFromAxisAngle (v3Right, turnSpeed) + DesiredEye3dRotation <- + if rotation'.Forward.Dot v3Up < 0.99f then rotation' + else + Quaternion.CreateFromAxisAngle (v3Down, 2.0f * MathF.Atan2(rotation.Z, rotation.W)) * + Quaternion.CreateFromAxisAngle (v3Right, MathF.PI_OVER_2) + if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.DownArrow else ImGuiKey.Q) && ImGui.IsCtrlUp () then + let rotation' = rotation * Quaternion.CreateFromAxisAngle (v3Left, turnSpeed) + DesiredEye3dRotation <- + if rotation'.Forward.Dot v3Down < 0.99f then rotation' + else + Quaternion.CreateFromAxisAngle (v3Up, 2.0f * MathF.Atan2(rotation.Z, rotation.W)) * + Quaternion.CreateFromAxisAngle (v3Right, -MathF.PI_OVER_2) + if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.E else ImGuiKey.UpArrow) && ImGui.IsAltUp () then + DesiredEye3dCenter <- center + v3Up.Transform rotation * moveSpeed + if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.Q else ImGuiKey.DownArrow) && ImGui.IsAltUp () then + DesiredEye3dCenter <- center + v3Down.Transform rotation * moveSpeed + if ImGui.IsKeyDown ImGuiKey.LeftArrow && ImGui.IsAltUp () then + DesiredEye3dRotation <- Quaternion.CreateFromAxisAngle (v3Up, turnSpeed) * rotation + if ImGui.IsKeyDown ImGuiKey.RightArrow && ImGui.IsAltUp () then + DesiredEye3dRotation <- Quaternion.CreateFromAxisAngle (v3Down, turnSpeed) * rotation + let private updateEntityContext world = if canEditWithMouse world then if ImGui.IsMouseReleased ImGuiMouseButton.Right then @@ -1570,67 +1632,48 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= | DragEntityPosition2d _ | DragEntityRotation2d _ -> DragEntityState <- DragEntityInactive | DragEntityInactive -> () - let private updateEyeDrag world = - if canEditWithMouse world then - if ImGui.IsMouseClicked ImGuiMouseButton.Middle then - let mousePositionInset = World.getMousePosition2dInset world - let dragState = DragEye2dCenter (world.Eye2dCenter + mousePositionInset, mousePositionInset) - DragEyeState <- dragState - match DragEyeState with - | DragEye2dCenter (entityDragOffset, mousePositionInsetOrig) -> - let mousePositionInset = World.getMousePosition2dInset world - DesiredEye2dCenter <- (entityDragOffset - mousePositionInsetOrig) + -Constants.Gaia.EyeSpeed * (mousePositionInset - mousePositionInsetOrig) - DragEyeState <- DragEye2dCenter (entityDragOffset, mousePositionInsetOrig) - | DragEyeInactive -> () - if ImGui.IsMouseReleased ImGuiMouseButton.Middle then - match DragEyeState with - | DragEye2dCenter _ -> DragEyeState <- DragEyeInactive - | DragEyeInactive -> () - - let private updateEyeTravel world = - if canEditWithKeyboard world then - let delta = world.DateDelta - let seconds = single delta.TotalSeconds - let center = world.Eye3dCenter - let rotation = world.Eye3dRotation - let moveSpeed = - if ImGui.IsEnterDown () && ImGui.IsShiftDown () then 512.0f * seconds - elif ImGui.IsEnterDown () then 64.0f * seconds - elif ImGui.IsShiftDown () then 1.0f * seconds - else 8.0f * seconds - let turnSpeed = - if ImGui.IsShiftDown () && ImGui.IsEnterUp () then 1.5f * seconds - else 3.0f * seconds - if ImGui.IsKeyDown ImGuiKey.W && ImGui.IsCtrlUp () && ImGui.IsAltUp () then - DesiredEye3dCenter <- center + v3Forward.Transform rotation * moveSpeed - if ImGui.IsKeyDown ImGuiKey.S && ImGui.IsCtrlUp () && ImGui.IsAltUp () then - DesiredEye3dCenter <- center + v3Back.Transform rotation * moveSpeed - if ImGui.IsKeyDown ImGuiKey.A && ImGui.IsCtrlUp () && ImGui.IsAltUp () then - DesiredEye3dCenter <- center + v3Left.Transform rotation * moveSpeed - if ImGui.IsKeyDown ImGuiKey.D && ImGui.IsCtrlUp () && ImGui.IsAltUp () then - DesiredEye3dCenter <- center + v3Right.Transform rotation * moveSpeed - if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.UpArrow else ImGuiKey.E) && ImGui.IsCtrlUp () then - let rotation' = rotation * Quaternion.CreateFromAxisAngle (v3Right, turnSpeed) - DesiredEye3dRotation <- - if rotation'.Forward.Dot v3Up < 0.99f then rotation' - else - Quaternion.CreateFromAxisAngle (v3Down, 2.0f * MathF.Atan2(rotation.Z, rotation.W)) * - Quaternion.CreateFromAxisAngle (v3Right, MathF.PI_OVER_2) - if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.DownArrow else ImGuiKey.Q) && ImGui.IsCtrlUp () then - let rotation' = rotation * Quaternion.CreateFromAxisAngle (v3Left, turnSpeed) - DesiredEye3dRotation <- - if rotation'.Forward.Dot v3Down < 0.99f then rotation' - else - Quaternion.CreateFromAxisAngle (v3Up, 2.0f * MathF.Atan2(rotation.Z, rotation.W)) * - Quaternion.CreateFromAxisAngle (v3Right, -MathF.PI_OVER_2) - if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.E else ImGuiKey.UpArrow) && ImGui.IsAltUp () then - DesiredEye3dCenter <- center + v3Up.Transform rotation * moveSpeed - if ImGui.IsKeyDown (if AlternativeEyeTravelInput then ImGuiKey.Q else ImGuiKey.DownArrow) && ImGui.IsAltUp () then - DesiredEye3dCenter <- center + v3Down.Transform rotation * moveSpeed - if ImGui.IsKeyDown ImGuiKey.LeftArrow && ImGui.IsAltUp () then - DesiredEye3dRotation <- Quaternion.CreateFromAxisAngle (v3Up, turnSpeed) * rotation - if ImGui.IsKeyDown ImGuiKey.RightArrow && ImGui.IsAltUp () then - DesiredEye3dRotation <- Quaternion.CreateFromAxisAngle (v3Down, turnSpeed) * rotation + let private updateAssetDrag world = + let io = ImGui.GetIO () + if (*ImGui.BeginDragDropViewport ()*)true then + match DragDropPayloadOpt with + | Some (DragDropAsset (_, assetTag)) when + ImGui.IsMouseReleased ImGuiMouseButton.Left && + not io.WantCaptureMouse && + (*not (NativePtr.isNullPtr (ImGui.AcceptDragDropPayload "Asset").NativePtr)*)true -> + match Metadata.tryGetMetadata assetTag with + | ValueSome metadata -> + match metadata with + | RawMetadata -> () + | TextureMetadata _ -> + RightClickPosition <- World.getMousePosition world + let entity = createEntity true false (Some (nameof StaticSpriteDispatcher)) None world + entity.SetStaticImage (AssetTag.specialize assetTag) world + entity.AutoBounds world + | TileMapMetadata _ -> + RightClickPosition <- World.getMousePosition world + let entity = createEntity true false (Some (nameof TileMapDispatcher)) None world + entity.SetTileMap (AssetTag.specialize assetTag) world + entity.AutoBounds world + | SpineSkeletonMetadata _ -> + RightClickPosition <- World.getMousePosition world + let entity = createEntity true false (Some (nameof SpineSkeletonDispatcher)) None world + entity.SetSpineSkeleton (AssetTag.specialize assetTag) world + entity.AutoBounds world + | StaticModelMetadata _ -> + RightClickPosition <- World.getMousePosition world + let entity = createEntity true false (Some (nameof StaticModelDispatcher)) None world + entity.SetStaticModel (AssetTag.specialize assetTag) world + entity.AutoBounds world + | AnimatedModelMetadata _ -> + RightClickPosition <- World.getMousePosition world + let entity = createEntity true false (Some (nameof AnimatedModelDispatcher)) None world + entity.SetAnimatedModel (AssetTag.specialize assetTag) world + entity.AutoBounds world + | SoundMetadata -> () + | SongMetadata -> () + | ValueNone -> () + | Some _ | None -> () + (*ImGui.EndDragDropTarget ()*) let private updateHotkeys entityHierarchyFocused world = if not (modal ()) then @@ -1783,9 +1826,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if ImGui.BeginDragDropTarget () then if not (NativePtr.isNullPtr (ImGui.AcceptDragDropPayload "Entity").NativePtr) then match DragDropPayloadOpt with - | Some payload -> - let sourceEntityAddressStr = payload - let sourceEntity = Nu.Entity sourceEntityAddressStr + | Some (DragDropEntity sourceEntity) -> if not (sourceEntity.GetProtected world) then if ImGui.IsCtrlDown () then let entityDescriptor = World.writeEntity false false EntityDescriptor.empty sourceEntity world @@ -1850,11 +1891,10 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ShowSelectedEntity <- true else Log.warn "Cannot mount an entity circularly." else MessageBoxOpt <- Some "Cannot relocate a protected simulant (such as an entity created by the ImSim or MMCC API)." - | None -> () + | Some _ | None -> () ImGui.EndDragDropTarget () if ImGui.BeginDragDropSource () then - let entityAddressStr = entity.EntityAddress |> scstringMemo |> Symbol.distill - DragDropPayloadOpt <- Some entityAddressStr + DragDropPayloadOpt <- Some (DragDropEntity entity) ImGui.Text (entity.Name + if ImGui.IsCtrlDown () then " (Copy)" else "") ImGui.SetDragDropPayload ("Entity", IntPtr.Zero, 0u) |> ignore ImGui.EndDragDropSource () @@ -2691,9 +2731,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if ImGui.BeginDragDropTarget () then if not (NativePtr.isNullPtr (ImGui.AcceptDragDropPayload "Entity").NativePtr) then match DragDropPayloadOpt with - | Some payload -> - let sourceEntityAddressStr = payload - let sourceEntity = Nu.Entity sourceEntityAddressStr + | Some (DragDropEntity sourceEntity) -> if not (sourceEntity.GetProtected world) then if ImGui.IsCtrlDown () then let entityDescriptor = World.writeEntity false false EntityDescriptor.empty sourceEntity world @@ -2727,7 +2765,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ShowSelectedEntity <- true else MessageBoxOpt <- Some "Cannot unparent an entity when there exists another unparented entity with the same name." else MessageBoxOpt <- Some "Cannot relocate a protected simulant (such as an entity created by the ImSim or MMCC API)." - | None -> () + | Some _ | None -> () ImGui.EndDragDropTarget () // entity editing @@ -2982,15 +3020,15 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= if isPropertyAssetTag && ImGui.BeginDragDropTarget () then if not (NativePtr.isNullPtr (ImGui.AcceptDragDropPayload "Asset").NativePtr) then match DragDropPayloadOpt with - | Some payload -> + | Some (DragDropAsset (assetTagStr, _)) -> let pasts = Pasts - try let propertyValueEscaped = payload + try let propertyValueEscaped = assetTagStr let propertyValueUnescaped = String.unescape propertyValueEscaped let propertyValue = converter.ConvertFromString propertyValueUnescaped setPropertyValue false propertyValue propertyDescriptor simulant world with _ -> Pasts <- pasts - | None -> () + | Some _ | None -> () ImGui.EndDragDropTarget () with :? TargetException as exn -> PropertyFocusedOpt <- None @@ -3417,7 +3455,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let packageNameText = if Symbol.shouldBeExplicit packageName then String.surround "\"" packageName else packageName let assetNameText = if Symbol.shouldBeExplicit assetName then String.surround "\"" assetName else assetName let assetTagStr = "[" + packageNameText + " " + assetNameText + "]" - DragDropPayloadOpt <- Some assetTagStr + DragDropPayloadOpt <- Some (DragDropAsset (assetTagStr, asset packageName assetName)) ImGui.Text assetTagStr ImGui.SetDragDropPayload ("Asset", IntPtr.Zero, 0u) |> ignore ImGui.EndDragDropSource () @@ -4163,8 +4201,15 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= updateEyeTravel world updateEntityContext world updateEntityDrag world + updateAssetDrag world updateHotkeys entityHierarchyFocused world + // HACK: because ImGui.BeginDragDropViewport isn't available yet - + // https://github.com/ocornut/imgui/issues/5204 + // we rely on the DragDropPayloadOpt value being Nonified with the following mouse input check. + // TODO: P0: get rid of this ASAP so we can rely on ImGui.AcceptDragDropPayload exclusively. + if not (ImGui.IsMouseDown ImGuiMouseButton.Left) then DragDropPayloadOpt <- None + // reloading dialogs if ReloadAssetsRequested > 0 then imGuiReloadingAssetsDialog world if ReloadCodeRequested > 0 then imGuiReloadingCodeDialog world diff --git a/Nu/Nu.Gaia/GaiaPrelude.fs b/Nu/Nu.Gaia/GaiaPrelude.fs index 065e9b48c1..11faa6f880 100644 --- a/Nu/Nu.Gaia/GaiaPrelude.fs +++ b/Nu/Nu.Gaia/GaiaPrelude.fs @@ -14,7 +14,7 @@ type DragEntityState = | DragEntityInactive type DragEyeState = - | DragEye2dCenter of Vector2 * Vector2 + | DragEye2dCenter of Offset : Vector2 * Origin : Vector2 | DragEyeInactive type [] GaiaState = diff --git a/Nu/Nu/World/WorldImGui.fs b/Nu/Nu/World/WorldImGui.fs index 5fc02393bb..0ed7615c46 100644 --- a/Nu/Nu/World/WorldImGui.fs +++ b/Nu/Nu/World/WorldImGui.fs @@ -529,13 +529,13 @@ module WorldImGui = let (edited4, value) = if not (NativePtr.isNullPtr (ImGui.AcceptDragDropPayload "Asset").NativePtr) then match context.DragDropPayloadOpt with - | Some payload -> - try let valueStrEscaped = payload + | Some (DragDropAsset (assetTagStr, _)) -> + try let valueStrEscaped = assetTagStr let valueStrUnescaped = String.unescape valueStrEscaped let value = converter.ConvertFromString valueStrUnescaped (true, value) with _ -> (false, value) - | None -> (false, value) + | Some _ | None -> (false, value) else (false, value) ImGui.EndDragDropTarget () (edited4, value) diff --git a/Nu/Nu/World/WorldImSim.fs b/Nu/Nu/World/WorldImSim.fs index 009e902292..58d4a578af 100644 --- a/Nu/Nu/World/WorldImSim.fs +++ b/Nu/Nu/World/WorldImSim.fs @@ -398,7 +398,7 @@ module WorldImSim = // create entity only when needed if entityCreation then let mountOpt = match mountOptOpt with ValueSome mountOpt -> mountOpt | ValueNone -> Some Address.parent - World.createEntity7 true typeof<'d>.Name mountOpt OverlayNameDescriptor.DefaultOverlay (Some entity.Surnames) entity.Group world |> ignore + World.createEntity7 true typeof<'d>.Name mountOpt DefaultOverlay (Some entity.Surnames) entity.Group world |> ignore // protect entity World.setEntityProtected true entity world |> ignore diff --git a/Nu/Nu/World/WorldPrelude.fs b/Nu/Nu/World/WorldPrelude.fs index 801b441afd..cac0b9d072 100644 --- a/Nu/Nu/World/WorldPrelude.fs +++ b/Nu/Nu/World/WorldPrelude.fs @@ -355,8 +355,8 @@ type SlideDescriptor = IdlingTime : GameTime SlideImageOpt : Image AssetTag option } -/// Describes the shape of a desired overlay. -type OverlayNameDescriptor = +/// Specifies an overlay. +type OverlayDescriptor = | NoOverlay | RoutedOverlay | DefaultOverlay diff --git a/Nu/Nu/World/WorldTypes.fs b/Nu/Nu/World/WorldTypes.fs index 4a817c809f..bf431d38f3 100644 --- a/Nu/Nu/World/WorldTypes.fs +++ b/Nu/Nu/World/WorldTypes.fs @@ -297,13 +297,20 @@ and [] Nav3d = Nav3dConfigOldOpt = None Nav3dMeshOpt = None } +/// Represents a payload for editor drag and drop operations. +and DragDropPayload = + | DragDropAsset of AssetTagStr : string * AssetTag : AssetTag + | DragDropEntity of Entity : Entity + | DragDropUserDefined of obj + /// Context for editing behavior. +/// TODO: add a way to post a drag-drop payload. and EditContext = { Snapshot : SnapshotType -> World -> unit FocusProperty : unit -> unit UnfocusProperty : unit -> unit SearchAssetViewer : unit -> unit - DragDropPayloadOpt : string option + DragDropPayloadOpt : DragDropPayload option SnapDrag : single SelectedScreen : Screen SelectedGroup : Group From e6b9c7c9bfe2a239ab3163b42f5774309ed90188 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 15 Nov 2025 03:22:17 -0500 Subject: [PATCH 44/52] Increased shadow map maxes from 9 each to 12 each. --- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- Nu/Nu.Gaia/Gaia.fs | 2 +- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- Nu/Nu/Core/Constants.fs | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedDeferredLighting.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardAnimated.glsl | 4 ++-- .../Assets/Default/PhysicallyBasedForwardStatic.glsl | 4 ++-- 53 files changed, 105 insertions(+), 105 deletions(-) diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Gaia/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 71d292679e..4a5ea68150 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -3367,7 +3367,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= | 0 -> Snaps2dSelected <- true | _ -> Snaps2dSelected <- false if ImGui.IsItemHovered ImGuiHoveredFlags.DelayNormal && ImGui.BeginTooltip () then - ImGui.Text "Use 2d or 3d snapping (F3 to swap mode)." + ImGui.Text "Use 2d or 3d snapping (F3 to switch)." ImGui.EndTooltip () ImGui.SameLine () let mutable (p, d, s) = if Snaps2dSelected then Snaps2d else Snaps3d diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Pipe/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.ImSim.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.ImSim.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.Mmcc.Empty/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Template.Mmcc.Game/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Nu/Nu.Tests/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Nu/Nu/Core/Constants.fs b/Nu/Nu/Core/Constants.fs index b5fde49dcc..48dc8d462c 100644 --- a/Nu/Nu/Core/Constants.fs +++ b/Nu/Nu/Core/Constants.fs @@ -211,8 +211,8 @@ module Render = let [] LightsMaxForward = 9 // NOTE: remember to update LIGHTS_MAX in forward shaders when changing this! let [] mutable ShadowVirtualResolution = match ConfigurationManager.AppSettings.["ShadowVirtualResolution"] with null -> 256 | value -> scvalue value let [] mutable ShadowDisplayScalarMax = match ConfigurationManager.AppSettings.["ShadowDisplayScalarMax"] with null -> 4 | value -> scvalue value - let [] ShadowTexturesMax = 9 // NOTE: remember to update SHADOW_TEXTURES_MAX in shaders when changing this! - let [] ShadowMapsMax = 9 // NOTE: remember to update SHADOW_MAPS_MAX in shaders when changing this! + let [] ShadowTexturesMax = 12 // NOTE: remember to update SHADOW_TEXTURES_MAX in shaders when changing this! + let [] ShadowMapsMax = 12 // NOTE: remember to update SHADOW_MAPS_MAX in shaders when changing this! let [] mutable ShadowDirectionalMarginRatioCull = match ConfigurationManager.AppSettings.["ShadowDirectionalMarginRatioCull"] with null -> 0.5f | value -> scvalue value let [] ShadowCascadesMax = 2 // NOTE: remember to update SHADOW_CASCADES_MAX in shaders when changing this! let [] ShadowCascadeLevels = 3 // NOTE: remember to update SHADOW_CASCADE_LEVELS_SIZE in shaders when changing this! diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Blaze Vector ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Blaze Vector Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Breakout ImSim/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Breakout Mmcc/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Jump Box/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Metrics/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Nelmish/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Sand Box 2d/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Terra Firma/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl index 1a3b687eaf..927c1b89d5 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedDeferredLighting.glsl @@ -19,8 +19,8 @@ const float PI = 3.141592654; const float PI_OVER_2 = PI / 2.0; const float ATTENUATION_CONSTANT = 1.0; const int LIGHTS_MAX = 64; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl index a3ed7f6db4..3225ba701f 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardAnimated.glsl @@ -87,8 +87,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; diff --git a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl index 3631a3106c..b0f618ce49 100644 --- a/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl +++ b/Projects/Twenty 48/Assets/Default/PhysicallyBasedForwardStatic.glsl @@ -68,8 +68,8 @@ const float ATTENUATION_CONSTANT = 1.0f; const float ENVIRONMENT_FILTER_REFRACTED_SATURATION = 2.0; const int LIGHT_MAPS_MAX = 2; const int LIGHTS_MAX = 9; -const int SHADOW_TEXTURES_MAX = 9; -const int SHADOW_MAPS_MAX = 9; +const int SHADOW_TEXTURES_MAX = 12; +const int SHADOW_MAPS_MAX = 12; const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05; // TODO: see if this should be proportionate to shadow texel size. const int SHADOW_CASCADES_MAX = 2; const int SHADOW_CASCADE_LEVELS = 3; From 9d583c4818f5be2b56b1a03bdf6a79d7efdf192c Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 15 Nov 2025 11:04:23 -0500 Subject: [PATCH 45/52] Constraing FOV value to avoid exceptions. --- Nu/Nu/World/WorldModuleGame.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/Nu/Nu/World/WorldModuleGame.fs b/Nu/Nu/World/WorldModuleGame.fs index 19af0828ea..dcdd50fb9d 100644 --- a/Nu/Nu/World/WorldModuleGame.fs +++ b/Nu/Nu/World/WorldModuleGame.fs @@ -374,6 +374,7 @@ module WorldModuleGame = (World.getGameState game world).Eye3dFieldOfView static member internal setGameEye3dFieldOfView value game world = + let value = value |> max 0.001f |> min MathF.PI_MINUS_EPSILON let gameState = World.getGameState game world let previous = gameState.Eye3dFieldOfView if previous <> value then From 0b1002c4b9155aade2704c037407fa7d112bbc26 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 15 Nov 2025 11:15:11 -0500 Subject: [PATCH 46/52] Keeping slider grabs from obscuring slider values. --- Nu/Nu/ImGui/ImGui.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nu/Nu/ImGui/ImGui.fs b/Nu/Nu/ImGui/ImGui.fs index 56bf55ad8d..23b1fab5fb 100644 --- a/Nu/Nu/ImGui/ImGui.fs +++ b/Nu/Nu/ImGui/ImGui.fs @@ -179,8 +179,8 @@ type ImGui (stub : bool, displaySize : Vector2i) = colors.[int ImGuiCol.ScrollbarGrabHovered] <- Vector4 (0.407843142747879f, 0.407843142747879f, 0.407843142747879f, 1.0f) colors.[int ImGuiCol.ScrollbarGrabActive] <- Vector4 (0.5098039507865906f, 0.5098039507865906f, 0.5098039507865906f, 1.0f) colors.[int ImGuiCol.CheckMark] <- Vector4 (1.0f, 1.0f, 1.0f, 1.0f) - colors.[int ImGuiCol.SliderGrab] <- Vector4 (0.8784313797950745f, 0.8784313797950745f, 0.8784313797950745f, 1.0f) - colors.[int ImGuiCol.SliderGrabActive] <- Vector4 (0.9803921580314636f, 0.9803921580314636f, 0.9803921580314636f, 1.0f) + colors.[int ImGuiCol.SliderGrab] <- Vector4 (0.8784313797950745f, 0.8784313797950745f, 0.8784313797950745f, 0.3499999940395355f) + colors.[int ImGuiCol.SliderGrabActive] <- Vector4 (0.9803921580314636f, 0.9803921580314636f, 0.9803921580314636f, 0.3098039329051971f) colors.[int ImGuiCol.Button] <- Vector4 (0.2980392277240754f, 0.2980392277240754f, 0.2980392277240754f, 0.6015625f) colors.[int ImGuiCol.ButtonHovered] <- Vector4 (0.494117647409439f, 0.494117647409439f, 0.494117647409439f, 0.6015625f) colors.[int ImGuiCol.ButtonActive] <- Vector4 (0.658823549747467f, 0.658823549747467f, 0.658823549747467f, 0.6015625f) From 5e48d4848bbb8fe5b9089bc917c032d8b5bbb9f8 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sat, 15 Nov 2025 15:08:38 -0500 Subject: [PATCH 47/52] Implemented #1215. --- Nu/Nu.Gaia/Gaia.fs | 1 + Nu/Nu/ImGui/ImGuiInterop.fs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 4a5ea68150..04665a54be 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1689,6 +1689,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= elif ImGui.IsKeyPressed ImGuiKey.F10 then setCaptureMode (not CaptureMode) world elif ImGui.IsKeyPressed ImGuiKey.F11 then setFreeMode (not FreeMode) world elif ImGui.IsKeyPressed ImGuiKey.F12 then OverlayMode <- not OverlayMode + elif ImGui.IsKeyPressed ImGuiKey.Escape then ImGuiInternal.tryCancelDragDrop (); DragDropPayloadOpt <- None // TODO: P0: remove DDPO <- None when AcceptDragDropPayload is exposed. elif ImGui.IsKeyPressed ImGuiKey.Enter && ImGui.IsCtrlUp () && ImGui.IsShiftUp () && ImGui.IsAltDown () then World.tryToggleWindowFullScreen world elif ImGui.IsKeyPressed ImGuiKey.UpArrow && ImGui.IsCtrlUp () && ImGui.IsShiftUp () && ImGui.IsAltDown () then tryReorderSelectedEntity true world elif ImGui.IsKeyPressed ImGuiKey.DownArrow && ImGui.IsCtrlUp () && ImGui.IsShiftUp () && ImGui.IsAltDown () then tryReorderSelectedEntity false world diff --git a/Nu/Nu/ImGui/ImGuiInterop.fs b/Nu/Nu/ImGui/ImGuiInterop.fs index b40137a838..f1694b820a 100644 --- a/Nu/Nu/ImGui/ImGuiInterop.fs +++ b/Nu/Nu/ImGui/ImGuiInterop.fs @@ -15,12 +15,27 @@ type Rect = [] module ImGuiInternal = + [] + extern void ImGui_ClearDragDrop () + + [] + extern void ImGui_ClearActiveID () + + [] + extern nativeint ImGui_GetDragDropPayload () + [] extern nativeint DockBuilder_GetCentralNode (uint32) [] extern void DockNode_GetRect (nativeint, nativeint) + /// Clear the drag drop action. + let tryCancelDragDrop () = + if ImGui_GetDragDropPayload () <> 0n then + ImGui_ClearActiveID () + ImGui_ClearDragDrop () + /// Try to get the bounds of the central dock node in the given dock space. let tryGetCentralDockNodeBounds dockSpaceId = let centralNode = DockBuilder_GetCentralNode dockSpaceId From b347623649df4c4253475fab891a6ded1cc3a082 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sun, 16 Nov 2025 02:27:52 -0500 Subject: [PATCH 48/52] Implemented #819 and #1065. --- Nu/Nu.Gaia/Gaia.fs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 04665a54be..c779362a9f 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -111,6 +111,7 @@ module Gaia = let mutable private EntityHierarchySearchStr = "" let mutable private PropagationSourcesSearchStr = "" let mutable private AssetViewerSearchStr = "" + let mutable private LoadPackageName = "" (* Project States *) @@ -3239,7 +3240,8 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.Text "Clear evaluation output (Alt+C)" ImGui.EndTooltip () if InteractiveInputFocusRequested then ImGui.SetKeyboardFocusHere (); InteractiveInputFocusRequested <- false - ImGui.InputTextMultiline ("##interactiveInputStr", &InteractiveInputStr, 131072u, v2 -1.0f 130.0f, if eval then ImGuiInputTextFlags.ReadOnly else ImGuiInputTextFlags.None) |> ignore + let inputTextHeight = ImGui.GetContentRegionAvail().Y * 0.65f + ImGui.InputTextMultiline ("##interactiveInputStr", &InteractiveInputStr, 131072u, v2 -1.0f inputTextHeight, if eval then ImGuiInputTextFlags.ReadOnly else ImGuiInputTextFlags.None) |> ignore if enter then InteractiveInputStr <- "" if eval || enter then InteractiveInputFocusRequested <- true ImGui.Separator () @@ -3405,15 +3407,19 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= let windowName = "Asset Viewer" if ImGui.Begin (windowName, ImGuiWindowFlags.NoNav) then if ImGui.IsWindowFocused () && SelectedWindowRestoreRequested = 0 then SelectedWindowOpt <- Some windowName - ImGui.SetNextItemWidth -1.0f + ImGui.SetNextItemWidth (ImGui.GetContentRegionAvail().X * 0.5f) let searchActivePrevious = not (String.IsNullOrWhiteSpace AssetViewerSearchStr) if AssetViewerSearchRequested then ImGui.SetKeyboardFocusHere () AssetViewerSearchStr <- "" AssetViewerSearchRequested <- false - ImGui.InputTextWithHint ("##assetViewerSearchStr", "[enter search text]", &AssetViewerSearchStr, 4096u) |> ignore + ImGui.InputTextWithHint ("##assetViewerSearchStr", "[search text]", &AssetViewerSearchStr, 4096u) |> ignore let searchActiveCurrent = not (String.IsNullOrWhiteSpace AssetViewerSearchStr) let searchDeactivated = searchActivePrevious && not searchActiveCurrent + ImGui.SameLine () + ImGui.SetNextItemWidth -1.0f + if ImGui.InputTextWithHint ("##loadPackageName", "[package to load]", &LoadPackageName, 4096u, ImGuiInputTextFlags.EnterReturnsTrue) then + Metadata.loadMetadataPackage LoadPackageName ImGui.BeginChild "Container" |> ignore for packageEntry in Metadata.getMetadataPackagesLoaded () |> Array.sortWith (fun a b -> String.Compare (a.Key, b.Key, true)) do let flags = ImGuiTreeNodeFlags.OpenOnArrow From 9bc5896ee986f9d7ce7b66768efc9c87e3637d69 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sun, 16 Nov 2025 02:50:30 -0500 Subject: [PATCH 49/52] Clearing package name upon load. --- Nu/Nu.Gaia/Gaia.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index c779362a9f..31ed3aac82 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -3420,6 +3420,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= ImGui.SetNextItemWidth -1.0f if ImGui.InputTextWithHint ("##loadPackageName", "[package to load]", &LoadPackageName, 4096u, ImGuiInputTextFlags.EnterReturnsTrue) then Metadata.loadMetadataPackage LoadPackageName + LoadPackageName <- "" ImGui.BeginChild "Container" |> ignore for packageEntry in Metadata.getMetadataPackagesLoaded () |> Array.sortWith (fun a b -> String.Compare (a.Key, b.Key, true)) do let flags = ImGuiTreeNodeFlags.OpenOnArrow From aef04c6f1599415d5070ebcd3c99dcde6dc7f784 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sun, 16 Nov 2025 23:45:42 -0500 Subject: [PATCH 50/52] Fixed regression where Esc in Gaia doesn't deselect current entity. --- Nu/Nu.Gaia/Gaia.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 31ed3aac82..654bc77d95 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1690,7 +1690,6 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= elif ImGui.IsKeyPressed ImGuiKey.F10 then setCaptureMode (not CaptureMode) world elif ImGui.IsKeyPressed ImGuiKey.F11 then setFreeMode (not FreeMode) world elif ImGui.IsKeyPressed ImGuiKey.F12 then OverlayMode <- not OverlayMode - elif ImGui.IsKeyPressed ImGuiKey.Escape then ImGuiInternal.tryCancelDragDrop (); DragDropPayloadOpt <- None // TODO: P0: remove DDPO <- None when AcceptDragDropPayload is exposed. elif ImGui.IsKeyPressed ImGuiKey.Enter && ImGui.IsCtrlUp () && ImGui.IsShiftUp () && ImGui.IsAltDown () then World.tryToggleWindowFullScreen world elif ImGui.IsKeyPressed ImGuiKey.UpArrow && ImGui.IsCtrlUp () && ImGui.IsShiftUp () && ImGui.IsAltDown () then tryReorderSelectedEntity true world elif ImGui.IsKeyPressed ImGuiKey.DownArrow && ImGui.IsCtrlUp () && ImGui.IsShiftUp () && ImGui.IsAltDown () then tryReorderSelectedEntity false world @@ -1727,6 +1726,8 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= elif not (String.IsNullOrWhiteSpace EntityHierarchySearchStr) then EntityHierarchySearchStr <- "" else + ImGuiInternal.tryCancelDragDrop () + DragDropPayloadOpt <- None // TODO: P0: DDPO check and assignment <- None when AcceptDragDropPayload is exposed. focusPropertyOpt None world selectEntityOpt None world From 71ea7704c3143cc86c5da6fa39dca22e2bb6a067 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Sun, 16 Nov 2025 23:47:56 -0500 Subject: [PATCH 51/52] Comment fix. --- Nu/Nu.Gaia/Gaia.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 654bc77d95..714893a45f 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1727,7 +1727,7 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1280,720 Split= EntityHierarchySearchStr <- "" else ImGuiInternal.tryCancelDragDrop () - DragDropPayloadOpt <- None // TODO: P0: DDPO check and assignment <- None when AcceptDragDropPayload is exposed. + DragDropPayloadOpt <- None // TODO: P0: remove this line when AcceptDragDropPayload is exposed. focusPropertyOpt None world selectEntityOpt None world From 161f201d83a61f45a66a02e17e843402eabb1b37 Mon Sep 17 00:00:00 2001 From: bryanedds Date: Mon, 17 Nov 2025 02:45:08 -0500 Subject: [PATCH 52/52] Comment fix. --- Nu/Nu/Render/RendererProcess.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nu/Nu/Render/RendererProcess.fs b/Nu/Nu/Render/RendererProcess.fs index 7b333128e0..0b9aaff2eb 100644 --- a/Nu/Nu/Render/RendererProcess.fs +++ b/Nu/Nu/Render/RendererProcess.fs @@ -441,7 +441,7 @@ type RendererThread () = // acknowledge swap request swapRequestAcknowledged <- true - // swap, optionally finishing + // swap match window with SglWindow window -> SDL.SDL_GL_SwapWindow window.SglWindow // clean up 3d