2 using System.Collections.Generic;
3 using System.ComponentModel;
5 using System.Threading.Tasks;
6 using SiliconStudio.Paradox;
7 using SiliconStudio.Paradox.DataModel;
8 using SiliconStudio.Paradox.Effects;
9 using SiliconStudio.Paradox.Effects.Modules;
10 using SiliconStudio.Paradox.Engine;
11 using SiliconStudio.Paradox.EntityModel;
12 using SiliconStudio.Paradox.Games;
13 using SiliconStudio.Paradox.Games.Collections;
14 using SiliconStudio.Core.Extensions;
15 using SiliconStudio.Paradox.Games.Mathematics;
16 using SiliconStudio.Paradox.Input;
17 using SiliconStudio.Shaders;
24 private class SelectedEntity
27 public Vector3 PickingObjectOrigin;
29 public SelectedEntity(
Entity entity)
35 private Entity translationGizmo;
36 private Entity rotationGizmo;
38 private SelectedEntity[] selectedEntities;
40 private Entity currentActiveGizmoEntity;
42 private static readonly PropertyKey<GizmoAction> GizmoActionKey =
new PropertyKey<GizmoAction>(
"GizmoAction", typeof(
ScriptDebug));
43 private static readonly PropertyKey<Color> GizmoColorKey =
new PropertyKey<Color>(
"GizmoColor", typeof(
ScriptDebug));
61 var entity =
new Entity(
"RotationGizmo");
65 var gizmoActions =
new[] { GizmoAction.RotationX, GizmoAction.RotationY, GizmoAction.RotationZ };
66 var orientationMatrices =
new[] { Matrix.Identity, Matrix.RotationZ((float)Math.PI * 0.5f), Matrix.RotationY(-(float)Math.PI * 0.5f) };
67 var colors =
new[] { Color.Green, Color.Blue, Color.Red };
69 var albedoMaterial =
new ShaderMixinSource()
73 new ShaderComposition(
"albedoDiffuse",
new ShaderClassSource(
"ComputeColorFixed",
MaterialKeys.DiffuseColor)),
74 new ShaderComposition(
"albedoSpecular",
new ShaderClassSource(
"ComputeColor")),
77 for (
int axis = 0; axis < 3; ++axis)
80 var circleEffectMeshData =
new EffectMeshData();
82 circleEffectMeshData.MeshData = MeshDataHelper.CreateCircle(20.0f, 32, colors[axis]);
83 circleEffectMeshData.EffectData =
new EffectData(
"Gizmo") { AlbedoMaterial = albedoMaterial };
85 var circleEntity =
new Entity(
"ArrowCone");
86 circleEntity.GetOrCreate(ModelComponent.Key).SubMeshes.Add(circleEffectMeshData);
87 circleEntity.Set(TransformationComponent.Key, TransformationMatrix.CreateComponent(orientationMatrices[axis]));
89 circleEntity.Set(GizmoColorKey, colors[axis]);
90 circleEntity.Set(GizmoActionKey, gizmoActions[axis]);
100 var entity =
new Entity(
"TranslationGizmo");
103 var gizmoActions =
new[] { GizmoAction.TranslationX, GizmoAction.TranslationY, GizmoAction.TranslationZ };
104 var colors =
new[] { Color.Green, Color.Blue, Color.Red };
105 var orientationMatrices =
new[] { Matrix.Identity, Matrix.RotationZ((float)Math.PI * 0.5f), Matrix.RotationY(-(float)Math.PI * 0.5f) };
107 var albedoMaterial =
new ShaderMixinSource()
110 "AlbedoSpecularBase",
111 new ShaderComposition(
"albedoDiffuse",
new ShaderClassSource(
"ComputeColorFixed",
MaterialKeys.DiffuseColor)),
112 new ShaderComposition(
"albedoSpecular",
new ShaderClassSource(
"ComputeColor")),
115 for (
int axis = 0; axis < 3; ++axis)
118 var lineEffectMeshData =
new EffectMeshData();
120 lineEffectMeshData.MeshData = MeshDataHelper.CreateLine(20.0f, colors[axis]);
121 lineEffectMeshData.EffectData =
new EffectData(
"Gizmo") { AlbedoMaterial = albedoMaterial };
123 var lineEntity =
new Entity(
"ArrowCone");
124 lineEntity.GetOrCreate(ModelComponent.Key).SubMeshes.Add(lineEffectMeshData);
125 lineEntity.Set(TransformationComponent.Key, TransformationMatrix.CreateComponent(orientationMatrices[axis]));
127 var coneEffectMeshData =
new EffectMeshData();
129 coneEffectMeshData.MeshData = MeshDataHelper.CreateCone(2.5f, 10.0f, 10, colors[axis]);
130 coneEffectMeshData.EffectData =
new EffectData(
"Gizmo") { AlbedoMaterial = albedoMaterial };
132 var coneEntity =
new Entity(
"ArrowBody");
133 coneEntity.GetOrCreate(ModelComponent.Key).SubMeshes.Add(coneEffectMeshData);
134 coneEntity.Set(TransformationComponent.Key, TransformationMatrix.CreateComponent(Matrix.Translation(20.0f, 0.0f, 0.0f) * orientationMatrices[axis]));
136 lineEntity.Set(GizmoColorKey, colors[axis]);
137 coneEntity.Set(GizmoColorKey, colors[axis]);
139 lineEntity.Set(GizmoActionKey, gizmoActions[axis]);
140 coneEntity.Set(GizmoActionKey, gizmoActions[axis]);
142 entity.Transformation.Children.Add(lineEntity.Transformation);
143 entity.Transformation.Children.Add(coneEntity.Transformation);
149 private void RefreshGizmos(
GizmoAction currentGizmoAction)
157 if (currentActiveGizmoEntity != null)
158 editorEntitySystem.Entities.Remove(currentActiveGizmoEntity);
162 currentActiveGizmoEntity = translationGizmo;
166 currentActiveGizmoEntity = rotationGizmo;
170 currentActiveGizmoEntity = null;
175 if (currentActiveGizmoEntity == null)
180 if (selectedEntities != null && (selectedTransformations = selectedEntities.Select(x => x.Entity.Transformation).Where(x => x != null && x.Value is TransformationTRS).ToArray()).Length > 0)
182 if (!editorEntitySystem.Entities.
Contains(currentActiveGizmoEntity))
183 editorEntitySystem.
Add(currentActiveGizmoEntity);
186 var gizmoTransformation = (TransformationTRS)currentActiveGizmoEntity.
Transformation.Value;
187 var translationSum =
Vector3.Zero;
190 foreach (var selectedTransformation in selectedTransformations)
194 var parentMatrix = selectedTransformation.WorldMatrix;
195 translationSum += parentMatrix.TranslationVector;
198 gizmoTransformation.Translation = translationSum / selectedTransformations.Length;
204 var view = renderingSetup.MainPlugin.ViewParameters.Get(TransformationKeys.View);
205 var worldView = view;
206 var position = Vector3.TransformCoordinate(gizmoTransformation.Translation, worldView);
207 gizmoTransformation.Scaling =
new Vector3(position.Z * 0.01f);
212 if (editorEntitySystem.Entities.
Contains(currentActiveGizmoEntity))
213 editorEntitySystem.Entities.
Remove(currentActiveGizmoEntity);
217 private void UpdateSelectedEntities(EngineContext engineContext,
Entity nextSelectedEntity)
219 if (engineContext.InputManager.IsKeyDown(
Keys.LeftShift))
222 if (!selectedEntities.Any(x => x.Entity == nextSelectedEntity))
223 selectedEntities = selectedEntities.Concat(
new[] { new SelectedEntity(nextSelectedEntity) }).ToArray();
225 selectedEntities = selectedEntities.Where(x => x.Entity != nextSelectedEntity).ToArray();
230 selectedEntities = nextSelectedEntity != null ?
new[] {
new SelectedEntity(nextSelectedEntity) } :
new SelectedEntity[0];
238 var gizmoTargetPlugin = (
RenderTargetsPlugin)engineContext.DataContext.RenderPassPlugins.TryGetValue(
"GizmoTargetPlugin");
239 if (gizmoTargetPlugin == null)
242 var pickingResults =
new Queue<PickingAction>();
244 var effectGizmo = engineContext.RenderContext.BuildEffect(
"Gizmo")
246 new BasicShaderPlugin(
247 new ShaderMixinSource()
257 effectGizmo.PickingPassMainPlugin = gizmoTargetPlugin;
261 engineContext.RenderContext.Effects.Add(effectGizmo);
264 editorEntitySystem.Processors.Add(
new MeshProcessor(engineContext.RenderContext, engineContext.AssetManager));
267 editorEntitySystem.Processors.Add(
new TransformationUpdateProcessor());
275 engineContext.DataContext.RenderPassPlugins.TryGetValue(
"PickingPlugin", out renderPassPlugin);
277 engineContext.DataContext.RenderPassPlugins.TryGetValue(
"MouseOverPickingPlugin", out renderPassPlugin);
278 var mouseOverPickingPlugin = renderPassPlugin as PickingPlugin;
281 var pickingGizmoOrigin =
new Vector3();
282 var previousMouseLocation =
new Vector2();
283 var currentGizmoAction = GizmoAction.None;
284 var nextGizmoAction = GizmoAction.None;
285 Entity nextSelectedEntity = null;
287 var previousMousePosition = Vector2.Zero;
291 await engineContext.Scheduler.NextFrame();
293 lock (pickingResults)
295 var mousePosition = engineContext.InputManager.MousePosition;
296 if (engineContext.InputManager.IsMouseButtonPressed(
MouseButton.Left))
298 pickingResults.Enqueue(
new PickingAction
300 Type = PickingActionType.MouseDown,
301 MouseLocation = mousePosition,
302 PickingResult = pickingPlugin.Pick(engineContext.InputManager.MousePosition),
305 else if (engineContext.InputManager.IsMouseButtonReleased(
MouseButton.Left))
307 pickingResults.Enqueue(
new PickingAction
309 Type = PickingActionType.MouseUp,
310 MouseLocation = mousePosition,
314 if (engineContext.InputManager.IsMouseButtonDown(
MouseButton.Left) && previousMousePosition != mousePosition)
316 pickingResults.Enqueue(
new PickingAction
318 Type = PickingActionType.MouseMove,
319 MouseLocation = mousePosition,
323 pickingResults.Enqueue(
new PickingAction
325 Type = PickingActionType.MouseOver,
326 MouseLocation = mousePosition,
327 PickingResult = mouseOverPickingPlugin.Pick(mousePosition)
330 previousMousePosition = mousePosition;
332 while (pickingResults.Count > 0 && (pickingResults.Peek().PickingResult == null || pickingResults.Peek().PickingResult.IsCompleted))
338 var pickingAction = pickingResults.Dequeue();
339 var pickedEntity = pickingAction.PickingResult != null ? GetPickedEntity(engineContext, pickingAction.PickingResult.Result.EffectMesh) : null;
340 switch (pickingAction.Type)
342 case PickingActionType.MouseOver:
346 nextGizmoAction = pickedEntity != null ? pickedEntity.Get(GizmoActionKey) :
GizmoAction.None;
347 UpdateGizmoHighlighting(nextGizmoAction);
348 if (pickedEntity != null)
349 pickingGizmoOrigin = pickingAction.PickingResult.Result.Position;
352 case PickingActionType.MouseDown:
353 nextSelectedEntity = pickedEntity;
354 previousMouseLocation = pickingAction.MouseLocation;
359 UpdateSelectedEntities(engineContext, nextSelectedEntity);
363 UpdateGizmoHighlighting(currentGizmoAction);
366 if (currentActiveGizmoActionMode ==
GizmoAction.Translation)
368 currentGizmoAction = GizmoAction.TranslationXY;
369 if (pickedEntity != null)
370 pickingGizmoOrigin = pickingAction.PickingResult.Result.Position;
372 else if (currentGizmoAction ==
GizmoAction.Rotation)
374 currentGizmoAction = GizmoAction.RotationZ;
378 if (selectedEntities != null && selectedEntities.Length > 0)
381 foreach (var selectedEntity
in selectedEntities)
383 var transformationComponent = selectedEntity.Entity.Transformation.Value as TransformationTRS;
384 selectedEntity.PickingObjectOrigin = transformationComponent != null ? transformationComponent.Translation : Vector3.Zero;
388 case PickingActionType.MouseMove:
391 currentGizmoAction = nextGizmoAction;
392 UpdateGizmoHighlighting(currentGizmoAction);
393 if (selectedEntities != null && selectedEntities.Length > 0)
399 foreach (var selectedEntity
in selectedEntities)
401 MoveEntity(engineContext, selectedEntity.Entity, currentGizmoAction, pickingGizmoOrigin, selectedEntity.PickingObjectOrigin, pickingAction.MouseLocation - previousMouseLocation);
406 foreach (var selectedEntity
in selectedEntities)
408 RotateEntity(engineContext, selectedEntity.Entity, currentGizmoAction, pickingAction.MouseLocation - previousMouseLocation);
411 previousMouseLocation = pickingAction.MouseLocation;
415 case PickingActionType.MouseUp:
419 UpdateSelectedEntities(engineContext, nextSelectedEntity);
423 currentGizmoAction = GizmoAction.None;
424 nextGizmoAction = GizmoAction.None;
425 UpdateGizmoHighlighting(nextGizmoAction);
426 nextSelectedEntity = null;
435 selectedEntities = SelectedEntities.Select(x =>
new SelectedEntity(x)).ToArray();
438 RefreshGizmos(currentGizmoAction);
439 editorEntitySystem.Update();
448 var transformationComponent = entity != null ? entity.Transformation : null;
449 if (delta ==
Vector2.Zero || transformationComponent == null)
452 var transformationComponentValues = transformationComponent.Value as TransformationTRS;
453 if (transformationComponentValues == null)
456 var viewParameters = renderingSetup.MainPlugin.ViewParameters;
458 transformationComponentValues.Translation = pickingObjectOrigin;
465 axes[axisCount++] = Vector3.UnitX;
467 axes[axisCount++] = Vector3.UnitY;
469 axes[axisCount++] = Vector3.UnitZ;
472 throw new NotImplementedException(
"Translation should only act on two axes.");
481 Vector3.TransformNormal(ref axes[0], ref viewProj, out projectedAxis);
482 var unitX = Vector3.TransformNormal(Vector3.UnitX, viewProj);
483 var unitY = Vector3.TransformNormal(Vector3.UnitY, viewProj);
484 var unitZ = Vector3.TransformNormal(Vector3.UnitZ, viewProj);
487 projectedAxis.Z = 0.0f;
493 projectedAxis.Normalize();
498 var dotX = Math.Abs(Vector3.Dot(unitX, projectedAxis));
499 var dotY = Math.Abs(Vector3.Dot(unitY, projectedAxis));
500 var dotZ = Math.Abs(Vector3.Dot(unitZ, projectedAxis));
502 if (dotX < dotY && dotX < dotZ)
503 axes[1] = Vector3.UnitX;
504 else if (dotY < dotZ)
505 axes[1] = Vector3.UnitY;
507 axes[1] = Vector3.UnitZ;
510 var parentMatrix = transformationComponent.Parent != null ? transformationComponent.Parent.WorldMatrix : Matrix.Identity;
511 parentMatrix.Invert();
513 transformationComponentValues.Translation += Vector3.TransformNormal(ComputePickingDelta(engineContext, axes[0], axes[1], ref viewProj, pickingGizmoOrigin, pickingObjectOrigin, delta), parentMatrix);
515 transformationComponentValues.Translation += Vector3.TransformNormal(ComputePickingDelta(engineContext, axes[1], axes[0], ref viewProj, pickingGizmoOrigin, pickingObjectOrigin, delta), parentMatrix);
519 private void RotateEntity(EngineContext engineContext,
Entity entity,
GizmoAction currentGizmoAction,
Vector2 delta)
522 var transformationComponent = entity != null ? entity.Transformation : null;
526 if (delta ==
Vector2.Zero || transformationComponent == null)
529 var transformationComponentValues = transformationComponent.Value as TransformationTRS;
530 if (transformationComponentValues == null)
533 Matrix currentRotationMatrix;
535 Vector3 pickingGizmoRotationLocal;
536 transformationComponentValues.LocalMatrix.DecomposeXYZ(out pickingGizmoRotationLocal);
538 var currentRotationMatrixLocal = Matrix.RotationX(pickingGizmoRotationLocal.X)
539 * Matrix.RotationY(pickingGizmoRotationLocal.Y)
540 * Matrix.RotationZ(pickingGizmoRotationLocal.Z);
542 var parentMatrix = transformationComponent.Parent != null ? transformationComponent.Parent.WorldMatrix : Matrix.Identity;
543 var parentMatrixInverse = Matrix.Invert(parentMatrix);
545 float deltaRotation = (delta.X + delta.Y) * 0.01f;
548 currentRotationMatrixLocal.DecomposeXYZ(out rotationBefore);
553 currentRotationMatrix = currentRotationMatrixLocal * parentMatrixInverse * Matrix.RotationX(deltaRotation) * parentMatrix;
555 else if (currentGizmoAction ==
GizmoAction.RotationY)
557 currentRotationMatrix = currentRotationMatrixLocal * parentMatrixInverse * Matrix.RotationY(deltaRotation) * parentMatrix;
559 else if (currentGizmoAction ==
GizmoAction.RotationZ)
561 currentRotationMatrix = currentRotationMatrixLocal * parentMatrixInverse * Matrix.RotationZ(deltaRotation) * parentMatrix;
565 throw new NotImplementedException();
569 currentRotationMatrix.DecomposeXYZ(out rotationAfter);
571 transformationComponentValues.RotationEuler += rotationAfter - rotationBefore;
574 private enum PickingActionType
582 private struct PickingAction
584 public PickingActionType Type;
606 private Entity GetPickedEntity(EngineContext engineContext, EffectMesh pickedEffectMesh)
608 if (pickedEffectMesh == null)
612 var pickedEntity = pickedEffectMesh.Get(PickingPlugin.AssociatedEntity);
613 if (pickedEntity == null)
616 var entities = engineContext.EntityManager.Entities.AsEnumerable();
617 if (editorEntitySystem != null)
618 entities = entities.Concat(editorEntitySystem.Entities);
619 foreach (var entity
in entities)
621 var meshComponent = entity.Get(ModelComponent.Key);
622 if (meshComponent == null)
625 if (!meshComponent.InstantiatedSubMeshes.Any(x => x.Value == pickedEffectMesh))
628 pickedEntity = entity;
635 private void UpdateGizmoHighlighting(
GizmoAction gizmoAction)
637 if (currentActiveGizmoEntity == null)
640 var gizmoTranslationHierarchicalParent = currentActiveGizmoEntity.Transformation;
642 foreach (var child
in gizmoTranslationHierarchicalParent.Children)
644 var childGizmoAction = child.Entity.Get(GizmoActionKey);
645 bool isActiveGizmo = ((childGizmoAction & gizmoAction) == childGizmoAction);
646 child.Entity.Get(ModelComponent.Key).MeshParameters.Set(
MaterialKeys.DiffuseColor, isActiveGizmo ?
Color.Yellow : child.Entity.Get(GizmoColorKey));
653 var plane =
new Plane(pickingGizmoOrigin, pickingGizmoOrigin + axis1, pickingGizmoOrigin + axis2);
657 var pickingStartTranslationScreenSpace = Vector3.TransformCoordinate(pickingGizmoOrigin, viewProj);
660 var mouseDelta =
new Vector3(delta.X / engineContext.RenderContext.Width * 2.0f,
661 -delta.Y / engineContext.RenderContext.Height * 2.0f,
663 var mousePickingPosition = pickingStartTranslationScreenSpace + mouseDelta;
664 var invViewProj = Matrix.Invert(viewProj);
665 mousePickingPosition.Z = 0.0f;
666 var picking1 = Vector3.TransformCoordinate(mousePickingPosition, invViewProj);
667 mousePickingPosition.Z = 0.1f;
668 var picking2 = Vector3.TransformCoordinate(mousePickingPosition, invViewProj);
671 var ray =
new Ray(picking1,
Vector3.Normalize(picking2 - picking1));
673 plane.Intersects(ref ray, out pickingDelta);
676 pickingDelta = Vector3.Dot(axis1, pickingDelta - pickingGizmoOrigin) * axis1;
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
PropertyChangedEventHandler PropertyChanged
Entity GenerateRotationGizmo()
Entity[] SelectedEntities
Gets or sets currently selected entity.
Game entity. It usually aggregates multiple EntityComponent
SiliconStudio.Paradox.Engine.MeshProcessor MeshProcessor
SiliconStudio.Paradox.Input.Keys Keys
GizmoAction ActiveGizmoActionMode
Gets or sets currently active picking mode (valid choices: None for selection, Translation, Rotation).
TransformationComponent Transformation
Gets or sets the Transformation associated to this entity. Added for convenience over usual Get/Set m...
Entity GenerateTranslationGizmo()
This processor will take care of adding/removing children of every Entity added/removed in the Entity...
static readonly RenderingSetup Singleton
Flags
Enumeration of the new Assimp's flags.
SiliconStudio.Core.Mathematics.Color Color
Manage a collection of entities.
void Remove(Entity entity)
Removes the entity from the EntitySystem. It works weither entity has a parent or not...
Level10 render pass using a depth buffer and a render target.
SiliconStudio.Core.Mathematics.Vector3 Vector3
void Add(Entity entity)
Adds the entity. If the Entity has a parent, its parent should be added (or TransformationComponent.Children) should be used.
bool Contains(Entity item)
Determines whether this instance contains the specified entity.
A container to handle a hierarchical collection of effect variables.
async Task ProcessGizmoAndPicking(EngineContext engineContext)