Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
PickingPlugin.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Threading.Tasks;
6 using SiliconStudio.Paradox.Effects.Modules;
7 using SiliconStudio.Paradox.EntityModel;
8 using SiliconStudio.Paradox.Games;
9 using SiliconStudio.Paradox.Graphics;
10 using SiliconStudio.Core;
11 using SiliconStudio.Core.Collections;
12 using SiliconStudio.Core.Mathematics;
13 
14 namespace SiliconStudio.Paradox.Effects
15 {
17  {
18  private UpdatePassesDelegate updatePassesAction;
19 
20  public static PropertyKey<Entity> AssociatedEntity = new PropertyKey<Entity>("AssociatedEntity", typeof(PickingPlugin));
21 
22  public static readonly ParameterKey<Vector2> PickingScreenPosition = ParameterKeys.Value(Vector2.Zero);
23  public static readonly ParameterKey<int> PickingFrameIndex = ParameterKeys.Value(0);
24  public static readonly ParameterKey<int> PickingMeshIndex = ParameterKeys.Value(0);
25  public static readonly ParameterKey<Matrix> PickingMatrix = ParameterKeys.Value(Matrix.Identity);
26 
27  private HashSet<Request> requestResults = new HashSet<Request>();
28  private List<Request> pendingRequests = new List<Request>();
29  private int currentPickingFrameIndex;
30 
31  /// <summary>
32  /// Gets or sets the main plugin this instance is attached to.
33  /// </summary>
34  /// <value>
35  /// The main plugin.
36  /// </value>
37  public MainPlugin MainPlugin { get; set; }
38 
39  public int PickingDistance { get; set; }
40 
41  public override void Load()
42  {
43  base.Load();
44 
45  if (OfflineCompilation)
46  return;
47 
48  var renderTargets = new RenderTarget[2];
49  DepthStencilBuffer depthStencilBuffer = null;
50  Texture2D depthStencilTexture = null;
51 
52  Parameters.AddSources(MainPlugin.ViewParameters);
53 
54  Parameters.RegisterParameter(EffectPlugin.BlendStateKey);
55 
56  var filteredPasses = new FastList<RenderPass>();
57 
58  RenderPass.UpdatePasses += updatePassesAction = (RenderPass currentRenderPass, ref FastList<RenderPass> currentPasses) =>
59  {
60  var originalPasses = currentPasses;
61  filteredPasses.Clear();
62  currentPasses = filteredPasses;
63 
64  Parameters.Set(PickingFrameIndex, ++currentPickingFrameIndex);
65  Request[] requests;
66 
67  lock (pendingRequests)
68  {
69  // No picking request or no mesh to pick?
70  if (pendingRequests.Count == 0)
71  return;
72 
73  requests = pendingRequests.ToArray();
74  pendingRequests.Clear();
75  }
76 
77  foreach (var request in requests)
78  {
79  requestResults.Add(request);
80  }
81 
82  if (originalPasses == null)
83  return;
84 
85  // Count mesh passes
86  int meshIndex = 0;
87  foreach (var pass in originalPasses)
88  {
89  meshIndex += pass.Passes.Count;
90  }
91 
92  // No mesh to pick?
93  if (meshIndex == 0)
94  return;
95 
96  // Copy mesh passes and assign indices
97  var meshPasses = new EffectMesh[meshIndex];
98  meshIndex = 0;
99  foreach (var pass in RenderPass.Passes)
100  {
101  throw new NotImplementedException();
102  //foreach (var effectMeshPass in pass.Meshes)
103  //{
104  // meshPasses[meshIndex] = (EffectMesh)effectMeshPass;
105  // // Prefix increment so that 0 means no rendering.
106  // effectMeshPass.Parameters.Set(PickingMeshIndex, ++meshIndex);
107  //}
108  }
109 
110  // For now, it generates one rendering per picking.
111  // It would be quite easy to optimize it by make Picking shader works on multiple picking points at a time.
112  foreach (var request in requests)
113  {
114  var pickingRenderPass = new RenderPass("Picking");
115 
116  pickingRenderPass.StartPass.AddFirst = (threadContext) =>
117  {
118  threadContext.GraphicsDevice.Clear(renderTargets[0], Color.Black);
119  threadContext.GraphicsDevice.Clear(renderTargets[1], Color.Black);
120  threadContext.Parameters.Set(PickingScreenPosition, request.Location);
121  threadContext.GraphicsDevice.SetViewport(new Viewport(0, 0, renderTargets[0].Description.Width, renderTargets[0].Description.Height));
122 
123  threadContext.GraphicsDevice.Clear(depthStencilBuffer, DepthStencilClearOptions.DepthBuffer);
124  threadContext.GraphicsDevice.SetRenderTargets(depthStencilBuffer, renderTargets);
125  };
126  pickingRenderPass.EndPass.AddLast = (threadContext) =>
127  {
128  threadContext.Parameters.Reset(PickingScreenPosition);
129  threadContext.GraphicsDevice.Copy(renderTargets[0].Texture, request.ResultTextures[0]);
130  threadContext.GraphicsDevice.Copy(renderTargets[1].Texture, request.ResultTextures[1]);
131  };
132  //pickingRenderPass.PassesInternal = originalPasses;
133  throw new NotImplementedException();
134 
135  request.MeshPasses = meshPasses;
136 
137  currentPasses.Add(pickingRenderPass);
138 
139  request.HasResults = true;
140 
141  // Wait 2 frames before pulling the results.
142  request.FrameCounter = 2;
143  }
144  };
145 
146  RenderSystem.GlobalPass.EndPass.AddLast = CheckPickingResults;
147 
148  var backBuffer = GraphicsDevice.BackBuffer;
149 
150  int pickingArea = 1 + PickingDistance * 2;
151  renderTargets[0] = Texture2D.New(GraphicsDevice, pickingArea, pickingArea, PixelFormat.R32_UInt, TextureFlags.ShaderResource | TextureFlags.RenderTarget).ToRenderTarget().KeepAliveBy(ActiveObjects);
152  renderTargets[1] = Texture2D.New(GraphicsDevice, pickingArea, pickingArea, PixelFormat.R32G32B32A32_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget).ToRenderTarget().KeepAliveBy(ActiveObjects);
153 
154  depthStencilTexture = Texture2D.New(GraphicsDevice, pickingArea, pickingArea, PixelFormat.D32_Float, TextureFlags.ShaderResource | TextureFlags.DepthStencil).KeepAliveBy(ActiveObjects);
155  depthStencilBuffer = depthStencilTexture.ToDepthStencilBuffer(false);
156 
157  Parameters.AddDynamic(PickingMatrix, ParameterDynamicValue.New(PickingScreenPosition, (ref Vector2 pickingPosition, ref Matrix picking) =>
158  {
159  // Move center to picked position, and zoom (it is supposed to stay per-pixel according to render target size)
160  picking = Matrix.Translation(1.0f - (pickingPosition.X) / backBuffer.Width * 2.0f, -1.0f + (pickingPosition.Y) / backBuffer.Height * 2.0f, 0.0f)
161  * Matrix.Scaling((float)backBuffer.Width / (float)pickingArea, (float)backBuffer.Height / (float)pickingArea, 1.0f);
162  }));
163  }
164 
165  public override void Unload()
166  {
167  if (!OfflineCompilation)
168  {
169  RenderPass.UpdatePasses = (UpdatePassesDelegate)Delegate.Remove(RenderPass.UpdatePasses, updatePassesAction);
170  Parameters.RemoveSource(MainPlugin.ViewParameters);
171  RenderSystem.GlobalPass.EndPass -= CheckPickingResults;
172  }
173 
174  base.Unload();
175  }
176 
177  private unsafe void CheckPickingResults(ThreadContext threadContext)
178  {
179  if (requestResults.Count == 0)
180  return;
181 
182  foreach (var request in requestResults.ToArray())
183  {
184  if (request.HasResults)
185  {
186  if (--request.FrameCounter > 0)
187  continue;
188 
189  // TODO rewrite picking with get/setdata
190 
191  //var mapResultMeshId = threadContext.GraphicsDevice.Map(request.ResultTextures[0], 0, MapMode.Read);
192  //var mapResultPosition = threadContext.GraphicsDevice.Map(request.ResultTextures[1], 0, MapMode.Read);
193 
194  //var meshIds = (byte*)mapResultMeshId.DataPointer;
195  //var positions = (byte*)mapResultPosition.DataPointer;
196 
197  //var pickingArea = 1 + 2 * PickingDistance;
198  //float bestPickingDistance = float.PositiveInfinity;
199  //int meshId = 0;
200  //var position = Vector3.Zero;
201  //for (int y = 0; y < pickingArea; ++y)
202  //{
203  // for (int x = 0; x < pickingArea; ++x)
204  // {
205  // var currentMeshId = *(int*)&meshIds[y * mapResultMeshId.RowPitch + x * sizeof(int)];
206  // var pickingDistance = new Vector2(x - PickingDistance, y - PickingDistance).LengthSquared();
207  // if (currentMeshId != 0 && pickingDistance < bestPickingDistance)
208  // {
209  // bestPickingDistance = pickingDistance;
210  // meshId = currentMeshId;
211  // position = *(Vector3*)&positions[y * mapResultPosition.RowPitch + x * sizeof(Vector4)];
212  // }
213  // }
214  //}
215 
216  //threadContext.GraphicsDevice.Unmap(request.ResultTextures[0], 0);
217  //threadContext.GraphicsDevice.Unmap(request.ResultTextures[1], 0);
218 
219  //Console.WriteLine("Picking: {0} new R32G32B32_Float({1}f,{2}f,{3}f)", meshId, position.X, position.Y, position.Z);
220 
221  //var stream = new FileStream("picking.txt", FileMode.Append);
222  //var streamWriter = new StreamWriter(stream);
223  //streamWriter.WriteLine("new R32G32B32_Float({0}f,{1}f,{2}f)", position.X, position.Y, position.Z);
224  //streamWriter.Flush();
225  //stream.Close();
226 
227  //request.PickedMesh = (meshId != 0) ? request.MeshPasses[meshId - 1].EffectMesh : null;
228  //request.PickedPosition = request.PickedMesh != null ? position : new Vector3(float.NaN, float.NaN, float.NaN);
229  }
230  else
231  {
232  request.PickedMesh = null;
233  request.PickedPosition = new Vector3(float.NaN, float.NaN, float.NaN);
234  }
235 
236  request.MeshPasses = null;
237 
238  request.ResultTextures[0].Release();
239  request.ResultTextures[1].Release();
240 
241  request.TaskCompletionSource.SetResult(true);
242 
243  requestResults.Remove(request);
244  }
245  }
246 
247  public async Task<Result> Pick(Vector2 location)
248  {
249  var request = new Request();
250  request.Location = location;
251 
252  // Create staging textures
253  int pickingArea = 1 + PickingDistance * 2;
254  request.ResultTextures = new Texture2D[2];
255  request.ResultTextures[0] = Texture2D.New(GraphicsDevice, pickingArea, pickingArea, PixelFormat.R32_UInt, TextureFlags.None, usage: GraphicsResourceUsage.Staging);
256  request.ResultTextures[0].Name = "PickingTextureStaging";
257 
258  request.ResultTextures[1] = Texture2D.New(GraphicsDevice, pickingArea, pickingArea, PixelFormat.R32G32B32A32_Float, TextureFlags.None, usage: GraphicsResourceUsage.Staging);
259  request.ResultTextures[1].Name = "PickingTextureStaging";
260 
261  request.ResultTextures[0].AddReference();
262  request.ResultTextures[1].AddReference();
263 
264  lock (pendingRequests)
265  {
266  pendingRequests.Add(request);
267  }
268 
269  // Wait for results
271 
272  return new Result { EffectMesh = request.PickedMesh, Position = request.PickedPosition };
273  }
274 
275  private class Request
276  {
277  public int FrameCounter;
278  public bool HasResults;
279  public Vector2 Location;
280  public Texture2D[] ResultTextures;
281  public TaskCompletionSource<bool> TaskCompletionSource = new TaskCompletionSource<bool>();
282  public EffectMesh[] MeshPasses;
283  public EffectMesh PickedMesh;
284  public Vector3 PickedPosition;
285  }
286 
287  public class Result
288  {
289  public EffectMesh EffectMesh;
291  }
292  }
293 }
Key of an effect parameter.
Definition: ParameterKey.cs:15
Represents a two dimensional mathematical vector.
Definition: Vector2.cs:42
static void Scaling(ref Vector3 scale, out Matrix result)
Creates a matrix that scales along the x-axis, y-axis, and y-axis.
Definition: Matrix.cs:2397
async Task< Result > Pick(Vector2 location)
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
System.Threading.Tasks.Task Task
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
Defines the window dimensions of a render-target surface onto which a 3D volume projects.
Definition: Viewport.cs:14
Plugin used for the main rendering view.
Definition: MainPlugin.cs:15
A Texture 2D frontend to SharpDX.Direct3D11.Texture2D.
Definition: Texture2D.cs:37
SiliconStudio.Core.Mathematics.Vector3 Vector3
RenderPass is a hierarchy that defines how to collect and render meshes.
Definition: RenderPass.cs:19
A class that represents a tag propety.
Definition: PropertyKey.cs:17
Base class for texture resources.
Definition: Texture.cs:38
Represents a 4x4 mathematical matrix.
Definition: Matrix.cs:47