private Entity CreateOriginAxis(Color color, int axis) { var axisColor = color.ToColor3(); if (GraphicsDevice != null) { axisColor = color.ToColor3().ToColorSpace(GraphicsDevice.ColorSpace); } var gridTexture = CreateTexture(GenerateAxisImage); var axisMaterial = CreateColoredTextureMaterial(gridTexture, 1); axisMaterial.Passes[0].Parameters.Set(TexturingKeys.Texture0, gridTexture); axisMaterial.Passes[0].Parameters.Set(MaterialKeys.Sampler.ComposeWith("i0"), GraphicsDevice.SamplerStates.AnisotropicWrap); axisMaterial.Passes[0].Parameters.Set(GridColorKey, Color4.PremultiplyAlpha(new Color4(axisColor, 1f))); var axisEntity = new Entity("Scene grid origin axis") { new ModelComponent { Model = new Model { axisMaterial, new Mesh { Draw = GeometricPrimitive.Plane.New(GraphicsDevice, GridBase, GridBase).ToMeshDraw() } }, RenderGroup = RenderGroup, } }; axisEntity.TransformValue.Scale = new Vector3(GridSize, 1f / GridBase, 1f / GridBase); if (axis != 0) { axisEntity.TransformValue.Rotation = Quaternion.RotationX(MathUtil.PiOverTwo) * Quaternion.RotationAxis(new Vector3 { [1 + (axis % 2)] = 1f }, MathUtil.PiOverTwo); } var axisEntityRoot = new Entity("Scene grid origin axis root"); axisEntityRoot.AddChild(axisEntity); return(axisEntityRoot); }
/// <summary> /// Highlights the given material, but only within the given entity. /// </summary> /// <param name="entity">The entity in which to highlight the given material. This method does nothing if it's null.</param> /// <param name="materialIndex">The index of the material to highlight in the given entity.</param> internal void HighlightMaterial(Entity entity, int materialIndex) { editor.Controller.EnsureGameAccess(); lock (lockObject) { if (entity == null || materialIndex < 0) { return; } var modelComponent = entity.Get <ModelComponent>(); if (modelComponent?.Model == null) { return; } var material = modelComponent.GetMaterial(materialIndex); if (material == null) { return; } editor.Controller.InvokeAsync(() => { HighlightRenderFeature.MaterialsHighlightedForModel.Add(material); highlightedMaterials.Add(material); HighlightRenderFeature.ModelHighlightColors[modelComponent] = Color4.PremultiplyAlpha(AssetHighlighter.DirectReferenceColor); highlightedModelComponents.Add(modelComponent); foreach (var selectedEntity in Selection.GetSelectedIds().Select(x => editor.Controller.FindGameSidePart(x)).Cast <Entity>()) { modelComponent = selectedEntity.Get <ModelComponent>(); if (modelComponent?.Model == null) { continue; } HighlightRenderFeature.ModelHighlightColors[modelComponent] = Color4.PremultiplyAlpha(AssetHighlighter.DirectReferenceColor); highlightedModelComponents.Add(modelComponent); } }); } }
private async Task HighlightMaterial(Game game, Material material, float duration) { // TODO: this could be factorized between the different highlighters if (material == null) { return; } var currentTime = 0.0f; while (game.IsRunning) { Color4 color; lock (highlightedMaterials) { if (!highlightedMaterials.TryGetValue(material, out color)) { HighlightRenderFeature.MaterialHighlightColors.Remove(material); return; } } var ratio = duration >= 0 ? 1.0f - currentTime / duration : 1.0f; if (ratio <= 0.0f) { lock (highlightedMaterials) { highlightedMaterials.Remove(material); } HighlightRenderFeature.MaterialHighlightColors.Remove(material); return; } var currentColor = new Color4(color.R, color.G, color.B, color.A * ratio); HighlightRenderFeature.MaterialHighlightColors[material] = Color4.PremultiplyAlpha(currentColor); await game.Script.NextFrame(); currentTime += (float)game.UpdateTime.Elapsed.TotalSeconds; } }
protected override void UpdateBase(Color3 gridColor, float sceneUnit) { var cameraService = Game.EditorServices.Get <IEditorGameCameraService>(); if (cameraService == null) { return; } // update the grid color GridMaterial.Passes[0].Parameters.Set(GridColorKey, Color4.PremultiplyAlpha(new Color4(gridColor, 0.35f))); // Determine the up vector depending on view matrix and projection mode // -> When orthographic, if we are looking along a coordinate axis, place the grid perpendicular to that axis. // -> Fall back to the default plane otherwise. var viewAxisIndex = 1; var upVector = DefaultUpVector; var viewInvert = Matrix.Invert(cameraService.ViewMatrix); if (cameraService.IsOrthographic) { for (var i = 0; i < 3; i++) { var coordinateAxis = new Vector3 { [i] = 1.0f }; var dotProduct = Vector3.Dot(viewInvert.Forward, coordinateAxis); if (Math.Abs(dotProduct) > 0.99f) { upVector = coordinateAxis; viewAxisIndex = i; } } } // Check if the inverted View Matrix is valid (since it will be use for mouse picking, check the translation vector only) if (float.IsNaN(viewInvert.TranslationVector.X) || float.IsNaN(viewInvert.TranslationVector.Y) || float.IsNaN(viewInvert.TranslationVector.Z)) { return; } // The position of the grid and the origin in the scene var snappedPosition = Vector3.Zero; var originPosition = Vector3.Zero; // Add a small offset along the Up axis to avoid depth-fight with objects positioned at height=0 snappedPosition[viewAxisIndex] = Math.Sign(viewInvert[3, viewAxisIndex]) * GridVerticalOffset * sceneUnit; // Move the grid origin in slightly in front the grid to have it in the foreground originPosition[viewAxisIndex] = snappedPosition[viewAxisIndex] + Math.Sign(viewInvert[3, viewAxisIndex]) * 0.001f * sceneUnit; // In orthographic mode, put the grid and origin in the very background of scene if (cameraService.IsOrthographic) { var dotProduct = Vector3.Dot(viewInvert.Forward, upVector); var cameraOffset = viewInvert.TranslationVector[viewAxisIndex]; originPosition[viewAxisIndex] = cameraOffset + Math.Sign(dotProduct) * cameraService.FarPlane * 0.90f; snappedPosition[viewAxisIndex] = cameraOffset + Math.Sign(dotProduct) * cameraService.FarPlane * 0.95f; } // Determine the intersection point of the center of the vieport with the grid plane var ray = EditorGameHelper.CalculateRayFromMousePosition(cameraService.Component, new Vector2(0.5f), viewInvert); var plane = new Plane(Vector3.Zero, upVector); var intersection = EditorGameHelper.ProjectOnPlaneWithLimitAngle(ray, plane, MaximumViewAngle); // Detemine the scale of the grid depending of the distance of the camera to the grid plane // For orthographic projections, use a distance close to the one, at which the perspective projection would map to the viewport area. var gridScale = sceneUnit; var distanceToGrid = cameraService.IsOrthographic ? cameraService.Component.OrthographicSize * 1.5f : (viewInvert.TranslationVector - intersection).Length(); if (distanceToGrid < 1.5f * sceneUnit) { gridScale = 0.1f * sceneUnit; } if (distanceToGrid > 40f * sceneUnit) { gridScale = 10f * sceneUnit; } if (distanceToGrid > 400f * sceneUnit) { gridScale = 100f * sceneUnit; } // Snap the grid the closest possible to the intersection point var gridStringLineUnit = gridScale; for (var i = 0; i < 3; i++) { if (viewAxisIndex != i) { snappedPosition[i] += (float)Math.Round(intersection[i] / gridStringLineUnit) * gridStringLineUnit; } } // Apply positions grid.Transform.Position = snappedPosition; originAxis.TransformValue.Position = originPosition; for (int axis = 0; axis < 3; axis++) { originAxes[axis].TransformValue.Position[axis] = snappedPosition[axis]; } // Apply the scale (Note: scale cannot be applied at root or sub-position is scaled too) grid.Transform.Scale = new Vector3(gridScale); for (int axis = 0; axis < 3; axis++) { originAxes[axis].TransformValue.Scale = new Vector3(gridScale); } // Determine and apply the rotation to the grid and origin axis entities SetPlaneEntityRotation(2, upVector, grid); for (var axis = 0; axis < 3; axis++) { SetPlaneEntityRotation((axis + 2) % 3, upVector, originAxes[axis]); } // Hide the vertical axis of the origin for (int axis = 0; axis < 3; axis++) { originAxes[axis].GetChild(0).Get <ModelComponent>().Enabled = axis != viewAxisIndex; } }