Browse Source

Object spawning & camera ray related code cleanup in the editor. Added toggle in spawn editor to choose whether to align objects by their AABB bottom center. Default on. Also raycast against the scene (similar as spawn mode) when using the file menu to instantiate a node. Closes #275.

Lasse Öörni 11 years ago
parent
commit
dbf9bca51a

+ 1 - 4
Bin/Data/Scripts/Editor/EditorGizmo.as

@@ -219,10 +219,7 @@ void UseGizmo()
     IntVector2 pos = ui.cursorPosition;
     IntVector2 pos = ui.cursorPosition;
     if (ui.GetElementAt(pos) !is null)
     if (ui.GetElementAt(pos) !is null)
         return;
         return;
-    IntRect view = activeViewport.viewport.rect;
-    Ray cameraRay = camera.GetScreenRay(
-        float(pos.x - view.left) / view.width,
-        float(pos.y - view.top) / view.height);
+    Ray cameraRay = GetActiveViewportCameraRay();
     float scale = gizmoNode.scale.x;
     float scale = gizmoNode.scale.x;
 
 
     // Recalculate axes only when not left-dragging
     // Recalculate axes only when not left-dragging

+ 35 - 2
Bin/Data/Scripts/Editor/EditorScene.as

@@ -329,7 +329,10 @@ void LoadNode(const String&in fileName)
     // Before instantiating, add object's resource path if necessary
     // Before instantiating, add object's resource path if necessary
     SetResourcePath(GetPath(fileName), true, true);
     SetResourcePath(GetPath(fileName), true, true);
 
 
-    Vector3 position = GetNewNodePosition();
+    Ray cameraRay = camera.GetScreenRay(0.5, 0.5); // Get ray at view center
+    Vector3 position, normal;
+    GetSpawnPosition(cameraRay, newNodeDistance, position, normal, 0, true);
+
     Node@ newNode = InstantiateNodeFromFile(file, position, Quaternion(), 1, instantiateMode);
     Node@ newNode = InstantiateNodeFromFile(file, position, Quaternion(), 1, instantiateMode);
     if (newNode !is null)
     if (newNode !is null)
     {
     {
@@ -359,6 +362,17 @@ Node@ InstantiateNodeFromFile(File@ file, const Vector3& position, const Quatern
     if (newNode !is null)
     if (newNode !is null)
     {
     {
         newNode.scale = newNode.scale * scaleMod;
         newNode.scale = newNode.scale * scaleMod;
+        if (alignToAABBBottom)
+        {
+            Drawable@ drawable = GetFirstDrawable(newNode);
+            if (drawable !is null)
+            {
+                BoundingBox aabb = drawable.worldBoundingBox;
+                Vector3 aabbBottomCenter(aabb.center.x, aabb.min.y, aabb.center.z);
+                Vector3 offset = aabbBottomCenter - newNode.worldPosition;
+                newNode.worldPosition = newNode.worldPosition - offset;
+            }
+        }
 
 
         // Create an undo action for the load
         // Create an undo action for the load
         CreateNodeAction action;
         CreateNodeAction action;
@@ -945,8 +959,27 @@ void UpdateSceneMru(String filename)
 
 
     uiRecentScenes.Insert(0, filename);
     uiRecentScenes.Insert(0, filename);
 
 
-    for(uint i=uiRecentScenes.length-1;i>=maxRecentSceneCount;i--)
+    for (uint i = uiRecentScenes.length - 1; i >= maxRecentSceneCount; i--)
         uiRecentScenes.Erase(i);
         uiRecentScenes.Erase(i);
 
 
     PopulateMruScenes();
     PopulateMruScenes();
 }
 }
+
+Drawable@ GetFirstDrawable(Node@ node)
+{
+    Array<Node@> nodes = node.GetChildren(true);
+    nodes.Insert(0, node);
+
+    for (uint i = 0; i < nodes.length; ++i)
+    {
+        Array<Component@> components = nodes[i].GetComponents();
+        for (uint j = 0; j < components.length; ++j)
+        {
+            Drawable@ drawable = cast<Drawable>(components[j]);
+            if (drawable !is null)
+                return drawable;
+        }
+    }
+    
+    return null;
+}

+ 87 - 54
Bin/Data/Scripts/Editor/EditorSpawn.as

@@ -5,7 +5,7 @@ LineEdit@ randomRotationY;
 LineEdit@ randomRotationZ;
 LineEdit@ randomRotationZ;
 LineEdit@ randomScaleMinEdit;
 LineEdit@ randomScaleMinEdit;
 LineEdit@ randomScaleMaxEdit;
 LineEdit@ randomScaleMaxEdit;
-LineEdit@ NumberSpawnedObjectsEdit;
+LineEdit@ numberSpawnedObjectsEdit;
 LineEdit@ spawnRadiusEdit;
 LineEdit@ spawnRadiusEdit;
 LineEdit@ spawnCountEdit;
 LineEdit@ spawnCountEdit;
 
 
@@ -16,6 +16,7 @@ float randomScaleMax = 1;
 uint spawnCount = 1;
 uint spawnCount = 1;
 float spawnRadius = 0;
 float spawnRadius = 0;
 bool useNormal = true;
 bool useNormal = true;
+bool alignToAABBBottom = true;
 
 
 uint numberSpawnedObjects = 1;
 uint numberSpawnedObjects = 1;
 Array<String> spawnedObjectsNames;
 Array<String> spawnedObjectsNames;
@@ -48,15 +49,17 @@ void CreateSpawnEditor()
     randomScaleMaxEdit.text = String(randomScaleMax);
     randomScaleMaxEdit.text = String(randomScaleMax);
     CheckBox@ useNormalToggle = spawnWindow.GetChild("UseNormal", true);
     CheckBox@ useNormalToggle = spawnWindow.GetChild("UseNormal", true);
     useNormalToggle.checked = useNormal;
     useNormalToggle.checked = useNormal;
+    CheckBox@ alignToAABBBottomToggle = spawnWindow.GetChild("AlignToAABBBottom", true);
+    alignToAABBBottomToggle.checked = alignToAABBBottom;
+
+    numberSpawnedObjectsEdit = spawnWindow.GetChild("NumberSpawnedObjects", true);
+    numberSpawnedObjectsEdit.text = String(numberSpawnedObjects);
 
 
-    NumberSpawnedObjectsEdit = spawnWindow.GetChild("NumberSpawnedObjects", true);
-    NumberSpawnedObjectsEdit.text = String(numberSpawnedObjects);
-    
     spawnRadiusEdit = spawnWindow.GetChild("SpawnRadius", true);
     spawnRadiusEdit = spawnWindow.GetChild("SpawnRadius", true);
     spawnCountEdit = spawnWindow.GetChild("SpawnCount", true);
     spawnCountEdit = spawnWindow.GetChild("SpawnCount", true);
     spawnRadiusEdit.text = String(spawnRadius);
     spawnRadiusEdit.text = String(spawnRadius);
     spawnCountEdit.text = String(spawnCount);
     spawnCountEdit.text = String(spawnCount);
-    
+
     SubscribeToEvent(randomRotationX, "TextChanged", "EditRandomRotation");
     SubscribeToEvent(randomRotationX, "TextChanged", "EditRandomRotation");
     SubscribeToEvent(randomRotationY, "TextChanged", "EditRandomRotation");
     SubscribeToEvent(randomRotationY, "TextChanged", "EditRandomRotation");
     SubscribeToEvent(randomRotationZ, "TextChanged", "EditRandomRotation");
     SubscribeToEvent(randomRotationZ, "TextChanged", "EditRandomRotation");
@@ -65,7 +68,8 @@ void CreateSpawnEditor()
     SubscribeToEvent(spawnRadiusEdit, "TextChanged", "EditSpawnRadius");
     SubscribeToEvent(spawnRadiusEdit, "TextChanged", "EditSpawnRadius");
     SubscribeToEvent(spawnCountEdit, "TextChanged", "EditSpawnCount");
     SubscribeToEvent(spawnCountEdit, "TextChanged", "EditSpawnCount");
     SubscribeToEvent(useNormalToggle, "Toggled", "ToggleUseNormal");
     SubscribeToEvent(useNormalToggle, "Toggled", "ToggleUseNormal");
-    SubscribeToEvent(NumberSpawnedObjectsEdit, "TextFinished", "UpdateNumberSpawnedObjects");
+    SubscribeToEvent(alignToAABBBottomToggle, "Toggled", "ToggleAlignToAABBBottom");
+    SubscribeToEvent(numberSpawnedObjectsEdit, "TextFinished", "UpdateNumberSpawnedObjects");
     SubscribeToEvent(spawnWindow.GetChild("SetSpawnMode", true), "Released", "SetSpawnMode");
     SubscribeToEvent(spawnWindow.GetChild("SetSpawnMode", true), "Released", "SetSpawnMode");
     RefreshPickedObjects();
     RefreshPickedObjects();
 }
 }
@@ -115,6 +119,11 @@ void ToggleUseNormal(StringHash eventType, VariantMap& eventData)
     useNormal = cast<CheckBox>(eventData["Element"].GetPtr()).checked;
     useNormal = cast<CheckBox>(eventData["Element"].GetPtr()).checked;
 }
 }
 
 
+void ToggleAlignToAABBBottom(StringHash eventType, VariantMap& eventData)
+{
+    alignToAABBBottom = cast<CheckBox>(eventData["Element"].GetPtr()).checked;
+}
+
 void UpdateNumberSpawnedObjects(StringHash eventType, VariantMap& eventData)
 void UpdateNumberSpawnedObjects(StringHash eventType, VariantMap& eventData)
 {
 {
     LineEdit@ edit = eventData["Element"].GetPtr();
     LineEdit@ edit = eventData["Element"].GetPtr();
@@ -230,10 +239,76 @@ void PlaceObject(Vector3 spawnPosition, Vector3 normal)
     }
     }
 }
 }
 
 
-Vector3 RandomizeSpawnPosition(const Vector3&in position)
+bool GetSpawnPosition(const Ray&in cameraRay, float maxDistance, Vector3&out position, Vector3&out normal, float randomRadius = 0.0,
+    bool allowNoHit = true)
+{
+    if (pickMode < PICK_RIGIDBODIES && editorScene.octree !is null)
+    {
+        RayQueryResult result = editorScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, maxDistance, DRAWABLE_GEOMETRY,
+            0x7fffffff);
+        if (result.drawable !is null)
+        {
+            if (randomRadius > 0)
+            {
+                Vector3 basePosition = RandomizeSpawnPosition(result.position, randomRadius);
+                basePosition.y += randomRadius;
+                result = editorScene.octree.RaycastSingle(Ray(basePosition, Vector3(0, -1, 0)), RAY_TRIANGLE, randomRadius * 2.0,
+                    DRAWABLE_GEOMETRY, 0x7fffffff);
+                if (result.drawable !is null)
+                {
+                    position = result.position;
+                    normal = result.normal;
+                    return true;
+                }
+            }
+            else
+            {
+                position = result.position;
+                normal = result.normal;
+                return true;
+            }
+        }
+    }
+    else if (editorScene.physicsWorld !is null)
+    {
+        // If we are not running the actual physics update, refresh collisions before raycasting
+        if (!runUpdate)
+            editorScene.physicsWorld.UpdateCollisions();
+
+        PhysicsRaycastResult result = editorScene.physicsWorld.RaycastSingle(cameraRay, maxDistance);
+
+        if (result.body !is null)
+        {
+            if (randomRadius > 0)
+            {
+                Vector3 basePosition = RandomizeSpawnPosition(result.position, randomRadius);
+                basePosition.y += randomRadius;
+                result = editorScene.physicsWorld.RaycastSingle(Ray(basePosition, Vector3(0, -1, 0)), randomRadius * 2.0);
+                if (result.body !is null)
+                {
+                    position = result.position;
+                    normal = result.normal;
+                    return true;
+                }
+            }
+            else
+            {
+                position = result.position;
+                normal = result.normal;
+                return true;
+            }
+        }
+    }
+
+    position = cameraRay.origin + cameraRay.direction * maxDistance;
+    normal = Vector3(0, 1, 0);
+    return allowNoHit;
+}
+
+Vector3 RandomizeSpawnPosition(const Vector3&in position, float randomRadius)
 {
 {
     float angle = Random() * 360.0;
     float angle = Random() * 360.0;
-    float distance = Random() * spawnRadius;
+    float distance = Random() * randomRadius;
     return position + Quaternion(0, angle, 0) * Vector3(0, 0, distance);
     return position + Quaternion(0, angle, 0) * Vector3(0, 0, distance);
 }
 }
 
 
@@ -245,52 +320,10 @@ void SpawnObject()
 
 
     for (uint i = 0; i < spawnCount; i++)
     for (uint i = 0; i < spawnCount; i++)
     {
     {
-        IntVector2 pos = IntVector2(ui.cursorPosition.x, ui.cursorPosition.y);
-        Ray cameraRay = camera.GetScreenRay(
-            float(pos.x - view.left) / view.width,
-            float(pos.y - view.top) / view.height);
-
-        if (pickMode < PICK_RIGIDBODIES)
-        {
-            if (editorScene.octree is null)
-                return;
-
-            RayQueryResult result = editorScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip,
-                pickModeDrawableFlags[pickMode], 0x7fffffff);
-
-            // Randomize in a circle around original hit position
-            if (spawnRadius > 0)
-            {
-                Vector3 position = RandomizeSpawnPosition(result.position);
-                position.y += spawnRadius;
-                result = editorScene.octree.RaycastSingle(Ray(position, Vector3(0, -1, 0)), RAY_TRIANGLE, spawnRadius * 2.0,
-                    pickModeDrawableFlags[pickMode], 0x7fffffff);
-            }
-
-            if (result.drawable !is null)
-                PlaceObject(result.position, result.normal);
-        }
-        else
-        {
-            if (editorScene.physicsWorld is null)
-                return;
-
-            // If we are not running the actual physics update, refresh collisions before raycasting
-            if (!runUpdate)
-                editorScene.physicsWorld.UpdateCollisions();
+        Ray cameraRay = GetActiveViewportCameraRay();
 
 
-            PhysicsRaycastResult result = editorScene.physicsWorld.RaycastSingle(cameraRay, camera.farClip);
-
-            // Randomize in a circle around original hit position
-            if (spawnRadius > 0)
-            {
-                Vector3 position = RandomizeSpawnPosition(result.position);
-                position.y += spawnRadius;
-                result = editorScene.physicsWorld.RaycastSingle(Ray(position, Vector3(0, -1, 0)), spawnRadius * 2.0);
-            }
-
-            if (result.body !is null)
-                PlaceObject(result.position, result.normal);
-        }
+        Vector3 position, normal;
+        if (GetSpawnPosition(cameraRay, camera.farClip, position, normal, spawnRadius, false))
+            PlaceObject(position, normal);
     }
     }
 }
 }

+ 11 - 48
Bin/Data/Scripts/Editor/EditorView.as

@@ -213,10 +213,6 @@ class ViewportContext
 
 
     void SetOrthographic(bool orthographic)
     void SetOrthographic(bool orthographic)
     {
     {
-        // This doesn't work that great
-        /* if (orthographic) */
-        /*     camera.orthoSize = (cameraNode.position - GetScreenCollision(IntVector2(viewport.rect.width, viewport.rect.height))).length; */
-
         camera.orthographic = orthographic;
         camera.orthographic = orthographic;
         UpdateSettingsUI();
         UpdateSettingsUI();
     }
     }
@@ -1376,6 +1372,15 @@ void ViewMouseClick()
     ViewRaycast(true);
     ViewRaycast(true);
 }
 }
 
 
+Ray GetActiveViewportCameraRay()
+{
+    IntRect view = activeViewport.viewport.rect;
+    return camera.GetScreenRay(
+        float(ui.cursorPosition.x - view.left) / view.width,
+        float(ui.cursorPosition.y - view.top) / view.height
+    );
+}
+
 void ViewRaycast(bool mouseClick)
 void ViewRaycast(bool mouseClick)
 {
 {
     // Ignore if UI has modal element
     // Ignore if UI has modal element
@@ -1388,7 +1393,7 @@ void ViewRaycast(bool mouseClick)
 
 
     IntVector2 pos = ui.cursorPosition;
     IntVector2 pos = ui.cursorPosition;
     UIElement@ elementAtPos = ui.GetElementAt(pos, pickMode != PICK_UI_ELEMENTS);
     UIElement@ elementAtPos = ui.GetElementAt(pos, pickMode != PICK_UI_ELEMENTS);
-    if(editMode==EDIT_SPAWN)
+    if (editMode == EDIT_SPAWN)
     {
     {
         if(mouseClick && input.mouseButtonPress[MOUSEB_LEFT] && elementAtPos is null)
         if(mouseClick && input.mouseButtonPress[MOUSEB_LEFT] && elementAtPos is null)
             SpawnObject();
             SpawnObject();
@@ -1401,7 +1406,6 @@ void ViewRaycast(bool mouseClick)
 
 
     DebugRenderer@ debug = editorScene.debugRenderer;
     DebugRenderer@ debug = editorScene.debugRenderer;
 
 
-
     if (pickMode == PICK_UI_ELEMENTS)
     if (pickMode == PICK_UI_ELEMENTS)
     {
     {
         bool leftClick = mouseClick && input.mouseButtonPress[MOUSEB_LEFT];
         bool leftClick = mouseClick && input.mouseButtonPress[MOUSEB_LEFT];
@@ -1426,10 +1430,7 @@ void ViewRaycast(bool mouseClick)
     if (elementAtPos !is null)
     if (elementAtPos !is null)
         return;
         return;
 
 
-    IntRect view = activeViewport.viewport.rect;
-    Ray cameraRay = camera.GetScreenRay(
-        float(pos.x - view.left) / view.width,
-        float(pos.y - view.top) / view.height);
+    Ray cameraRay = GetActiveViewportCameraRay();
     Component@ selectedComponent;
     Component@ selectedComponent;
 
 
     if (pickMode < PICK_RIGIDBODIES)
     if (pickMode < PICK_RIGIDBODIES)
@@ -1615,44 +1616,6 @@ Vector3 SelectedNodesCenterPoint()
         return centerPoint;
         return centerPoint;
 }
 }
 
 
-Vector3 GetScreenCollision(IntVector2 pos)
-{
-    Ray cameraRay = camera.GetScreenRay(float(pos.x) / activeViewport.viewport.rect.width, float(pos.y) / activeViewport.viewport.rect.height);
-    Vector3 res = cameraNode.position + cameraRay.direction * Vector3(0, 0, newNodeDistance);
-
-    bool physicsFound = false;
-    if (editorScene.physicsWorld !is null)
-    {
-        if (!runUpdate)
-            editorScene.physicsWorld.UpdateCollisions();
-
-        PhysicsRaycastResult result = editorScene.physicsWorld.RaycastSingle(cameraRay, camera.farClip);
-
-        if (result.body !is null)
-        {
-            physicsFound = true;
-            result.position;
-        }
-    }
-
-    if (editorScene.octree is null)
-        return res;
-
-    RayQueryResult result = editorScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip,
-        DRAWABLE_GEOMETRY, 0x7fffffff);
-
-    if (result.drawable !is null)
-    {
-        // take the closer of the results
-        if (physicsFound && (cameraNode.position - res).length < (cameraNode.position - result.position).length)
-            return res;
-        else
-            return result.position;
-    }
-
-    return res;
-}
-
 void HandleBeginViewUpdate(StringHash eventType, VariantMap& eventData)
 void HandleBeginViewUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
     // Hide gizmo and grid from preview camera
     // Hide gizmo and grid from preview camera

+ 14 - 0
Bin/Data/UI/EditorSpawnWindow.xml

@@ -160,6 +160,20 @@
 						<attribute name="Text" value="Normal vector oriented" />
 						<attribute name="Text" value="Normal vector oriented" />
 					</element>
 					</element>
 				</element>
 				</element>
+				<element style="ListRow">
+					<attribute name="Min Size" value="200 17" />
+					<attribute name="Layout Spacing" value="8" />
+					<element type="CheckBox">
+						<attribute name="Name" value="AlignToAABBBottom" />
+					</element>
+					<element type="Text">
+						<attribute name="Top Left Color" value="0.85 0.85 0.85 1" />
+						<attribute name="Top Right Color" value="0.85 0.85 0.85 1" />
+						<attribute name="Bottom Left Color" value="0.85 0.85 0.85 1" />
+						<attribute name="Bottom Right Color" value="0.85 0.85 0.85 1" />
+						<attribute name="Text" value="Align to AABB bottom" />
+					</element>
+				</element>
 				<element style="ListRow">
 				<element style="ListRow">
 					<attribute name="Min Size" value="220 17" />
 					<attribute name="Min Size" value="220 17" />
 					<attribute name="Layout Spacing" value="20" />
 					<attribute name="Layout Spacing" value="20" />