Jypeli  5
The simple game programming library
Renderer.cs
Siirry tämän tiedoston dokumentaatioon.
1 #region MIT License
2 /*
3  * Copyright (c) 2009 University of Jyväskylä, Department of Mathematical
4  * Information Technology.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #endregion
25 
26 /*
27  * Authors: Tero Jäntti, Tomi Karppinen, Janne Nikkanen.
28  */
29 
30 using System;
31 using System.Diagnostics;
32 using System.Collections.Generic;
33 using Microsoft.Xna.Framework;
34 using Microsoft.Xna.Framework.Graphics;
35 using Jypeli.Effects;
36 
37 using XnaV2 = Microsoft.Xna.Framework.Vector2;
38 using XnaV3 = Microsoft.Xna.Framework.Vector3;
39 using XnaColor = Microsoft.Xna.Framework.Color;
40 
41 
42 namespace Jypeli
43 {
51  public static class Renderer
52  {
56  static readonly VertexPositionTexture[] textureVertices = new VertexPositionTexture[]
57  {
58  new VertexPositionTexture( new XnaV3(-0.5f, 0.5f, 0), new XnaV2(0.0f, 0.0f) ),
59  new VertexPositionTexture( new XnaV3(-0.5f, -0.5f, 0), new XnaV2(0.0f, 1.0f) ),
60  new VertexPositionTexture( new XnaV3(0.5f, 0.5f, 0), new XnaV2(1.0f, 0.0f) ),
61  new VertexPositionTexture( new XnaV3(0.5f, -0.5f, 0), new XnaV2(1.0f, 1.0f) )
62  };
63 
67  static readonly Int16[] textureTriangleIndices = new short[]
68  {
69  0, 1, 2,
70  1, 3, 2
71  };
72 
73  public static bool LightingEnabled { get; set; }
74 
75  static readonly BlendState NoDrawingToScreenBufferBlendState = new BlendState
76  {
77  ColorWriteChannels = ColorWriteChannels.None,
78  };
79 
80  static readonly DepthStencilState drawShapeToStencilBufferState = new DepthStencilState
81  {
82  StencilEnable = true,
83  ReferenceStencil = 0,
84  StencilFunction = CompareFunction.Equal,
85  StencilPass = StencilOperation.IncrementSaturation,
86  };
87 
88  static readonly DepthStencilState drawAccordingToStencilBufferState = new DepthStencilState
89  {
90  StencilEnable = true,
91  ReferenceStencil = 1,
92  StencilFunction = CompareFunction.LessEqual,
93  StencilPass = StencilOperation.Keep,
94  };
95 
96  private static bool isDrawingInsideShape = false;
97  private static DepthStencilState currentStencilState = DepthStencilState.None;
98 
99  private static VertexPositionTexture[] MakeTextureVertices( Vector wrapSize )
100  {
101  VertexPositionTexture[] tempVertices = new VertexPositionTexture[textureVertices.Length];
102  for ( int i = 0; i < textureVertices.Length; i++ )
103  {
104  tempVertices[i].Position = textureVertices[i].Position;
105  }
106 
107  float px = MathHelper.Clamp( (float)wrapSize.X, -1, 1 );
108  float py = MathHelper.Clamp( (float)wrapSize.Y, -1, 1 );
109 
110  // Since the origin in texture coordinates is at upper left corner,
111  // but at center in an object's coordinates, we need to make some
112  // adjustments here. Also, this makes partial textures possible.
113  float left = -(float)Math.Sign( wrapSize.X ) / 2 + 0.5f;
114  float right = left + px;
115  float top = -(float)Math.Sign( wrapSize.Y ) / 2 + 0.5f;
116  float bottom = top + py;
117 
118  tempVertices[0].TextureCoordinate = new XnaV2( left, top );
119  tempVertices[1].TextureCoordinate = new XnaV2( left, bottom );
120  tempVertices[2].TextureCoordinate = new XnaV2( right, top );
121  tempVertices[3].TextureCoordinate = new XnaV2( right, bottom );
122 
123  return tempVertices;
124  }
125 
126  public static void DrawImage( Image texture, ref Matrix matrix, Vector wrapSize )
127  {
128  if ( wrapSize.X == 0 || wrapSize.Y == 0 ) return;
129 
130  var device = Game.GraphicsDevice;
131  var tempVertices = MakeTextureVertices( wrapSize );
132 
133  device.RasterizerState = RasterizerState.CullClockwise;
134 #if WINDOWS_PHONE
135  // The WP7 emulator interprets cullmodes incorectly sometimes.
136  device.RasterizerState = RasterizerState.CullNone;
137 #endif
138 
139  device.BlendState = BlendState.AlphaBlend;
140 
141  float wrapX = (float)Math.Abs( wrapSize.X );
142  float wrapY = (float)Math.Abs( wrapSize.Y );
143 
144  if ( wrapX <= 1 && wrapY <= 1 )
145  {
146  // Draw only once
147  DrawImageTexture( texture, matrix, device, tempVertices );
148  return;
149  }
150 
151  float wx = (float)( Math.Sign( wrapSize.X ) );
152  float wy = (float)( Math.Sign( wrapSize.Y ) );
153  float tileW = 1 / wrapX;
154  float tileH = 1 / wrapY;
155  float topLeftX = -0.5f + 0.5f * tileW;
156  float topLeftY = 0.5f - 0.5f * tileH;
157  float partX = wrapX - (int)wrapX;
158  float partY = wrapY - (int)wrapY;
159 
160  for ( int y = 0; y < (int)wrapY; y++ )
161  {
162  for ( int x = 0; x < (int)wrapX; x++ )
163  {
164  Matrix m =
165  Matrix.CreateScale( 1 / wrapX, 1 / wrapY, 1 ) *
166  Matrix.CreateTranslation( topLeftX + x * tileW, topLeftY - y * tileH, 0 ) *
167  matrix;
168  DrawImageTexture( texture, m, device, tempVertices );
169  }
170 
171  if ( partX > 0 )
172  {
173  // Draw a partial horizontal tile
174  Matrix m =
175  Matrix.CreateScale( partX, 1, 1 ) *
176  Matrix.CreateScale( 1 / wrapX, 1 / wrapY, 1 ) *
177  Matrix.CreateTranslation( -tileW / 2 + tileW * partX / 2, 0, 0 ) *
178  Matrix.CreateTranslation( topLeftX + (int)wrapX * tileW, topLeftY - y * tileH, 0 ) *
179  matrix;
180 
181  DrawImage( texture, ref m, new Vector( wx * partX, wy ) );
182  }
183  }
184 
185  if ( partY > 0 )
186  {
187  for ( int x = 0; x < (int)wrapX; x++ )
188  {
189  // Draw a partial vertical tile
190  Matrix m =
191  Matrix.CreateScale( 1, partY, 1 ) *
192  Matrix.CreateScale( 1 / wrapX, 1 / wrapY, 1 ) *
193  Matrix.CreateTranslation( 0, tileH / 2 - tileH * partY / 2, 0 ) *
194  Matrix.CreateTranslation( topLeftX + x * tileW, topLeftY - (int)wrapY * tileH, 0 ) *
195  matrix;
196 
197  DrawImage( texture, ref m, new Vector( wx, wy * partY ) );
198  }
199 
200  if ( partX > 0 )
201  {
202  // Draw a partial diagonal tile
203  Matrix m =
204  Matrix.CreateScale( partX, partY, 1 ) *
205  Matrix.CreateScale( 1 / wrapX, 1 / wrapY, 1 ) *
206  Matrix.CreateTranslation( -tileW / 2 + tileW * partX / 2, tileH / 2 - tileH * partY / 2, 0 ) *
207  Matrix.CreateTranslation( topLeftX + (int)wrapX * tileW, topLeftY - (int)wrapY * tileH, 0 ) *
208  matrix;
209 
210  DrawImage( texture, ref m, new Vector( wx * partX, wy * partY ) );
211  }
212  }
213  }
214 
215  private static void DrawImageTexture( Image texture, Matrix matrix, GraphicsDevice device, VertexPositionTexture[] tempVertices )
216  {
217  Effect effect = Graphics.GetTextureEffect( ref matrix, texture.XNATexture, LightingEnabled );
218  foreach ( EffectPass pass in effect.CurrentTechnique.Passes )
219  {
220  pass.Apply();
221  device.SamplerStates[0] = Graphics.GetDefaultSamplerState();
222  Game.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(
223  PrimitiveType.TriangleList,
224  tempVertices, 0, tempVertices.Length,
225  textureTriangleIndices, 0, textureTriangleIndices.Length / 3
226  );
227  }
228  }
229 
239  public static void BeginDrawingInsideShape( Shape shape, ref Matrix transformation )
240  {
241  if ( shape.Cache.Triangles == null )
242  throw new ArgumentException( "The shape must have triangles" );
243  if ( isDrawingInsideShape )
244  throw new Exception( "EndDrawingInsideShape must be called before calling this function again" );
245 
246  isDrawingInsideShape = true;
247  var device = Game.GraphicsDevice;
248 
249  device.Clear( ClearOptions.Stencil, Color.Black.AsXnaColor(), 0, 0 );
250  device.DepthStencilState = currentStencilState = drawShapeToStencilBufferState;
251 
252  DrawFilledShape( shape.Cache, ref transformation, Color.White, NoDrawingToScreenBufferBlendState );
253 
254  device.DepthStencilState = currentStencilState = drawAccordingToStencilBufferState;
255  }
256 
257  public static void EndDrawingInsideShape()
258  {
259  if ( !isDrawingInsideShape )
260  throw new Exception( "BeginDrawingInsideShape must be called first" );
261 
262  Game.GraphicsDevice.DepthStencilState = currentStencilState = DepthStencilState.None;
263  isDrawingInsideShape = false;
264  }
265 
274  internal static void DrawText( string text, ref Matrix transformation, Font font, Color color )
275  {
276  SpriteBatch spriteBatch = Graphics.SpriteBatch;
277  spriteBatch.Begin( SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.LinearClamp, currentStencilState, RasterizerState.CullCounterClockwise, null, transformation );
278  spriteBatch.DrawString( font.XnaFont, text, Vector2.Zero, color.AsXnaColor() );
279  spriteBatch.End();
280  }
281 
285  public static void DrawShape( Shape shape, ref Matrix transformation, ref Matrix textureTransformation, Image texture, Vector textureWrapSize, Color color )
286  {
287  BeginDrawingInsideShape( shape, ref transformation );
288  DrawImage( texture, ref textureTransformation, textureWrapSize );
290  }
291 
292  public static void DrawShape( Shape shape, ref Matrix matrix, Color color )
293  {
294  if ( shape is RaySegment )
295  {
296  DrawRaySegment( (RaySegment)shape, ref matrix, color );
297  }
298  else if ( shape.Cache.Triangles != null )
299  {
300  DrawFilledShape( shape.Cache, ref matrix, color );
301  }
302  else
303  {
304  DrawPolygon( shape.Cache.OutlineVertices, ref matrix, color );
305  }
306  }
307 
308  public static void DrawRaySegment( RaySegment segment, ref Matrix matrix, Color color )
309  {
310  var device = Game.GraphicsDevice;
311 
312  Vector endPoint = segment.Origin + segment.Direction * segment.Length;
313 
314  VertexPositionColor[] colorVertices = new VertexPositionColor[2];
315  colorVertices[0].Position = new Vector3( (float)segment.Origin.X, (float)segment.Origin.Y, 0 );
316  colorVertices[0].Color = color.AsXnaColor();
317  colorVertices[1].Position = new Vector3( (float)endPoint.X, (float)endPoint.Y, 0 );
318  colorVertices[1].Color = color.AsXnaColor();
319 
320  BasicEffect effect = Graphics.BasicColorEffect;
321  effect.World = matrix;
322  foreach ( EffectPass pass in effect.CurrentTechnique.Passes )
323  {
324  pass.Apply();
325  device.SamplerStates[0] = SamplerState.LinearClamp;
326  device.DrawUserPrimitives<VertexPositionColor>( PrimitiveType.LineStrip, colorVertices, 0, 1 );
327  }
328  }
329 
333  internal static void DrawRectangle( ref Matrix matrix, Color color )
334  {
335  Vector[] vertices = new Vector[]
336  {
337  new Vector(-0.5, 0.5),
338  new Vector(-0.5, -0.5),
339  new Vector(0.5, -0.5),
340  new Vector(0.5, 0.5),
341  };
342  Renderer.DrawPolygon( vertices, ref matrix, color );
343  }
344 
345  internal static void DrawFilledShape( ShapeCache cache, ref Matrix matrix, Color color )
346  {
347  DrawFilledShape( cache, ref matrix, color, BlendState.NonPremultiplied );
348  }
349 
350  internal static void DrawFilledShape( ShapeCache cache, ref Matrix matrix, Color color, BlendState blendState )
351  {
352  var device = Game.GraphicsDevice;
353 
354  VertexPositionColor[] vertices = new VertexPositionColor[cache.Vertices.Length];
355  for ( int i = 0; i < vertices.Length; i++ )
356  {
357  Vector v = cache.Vertices[i];
358  vertices[i] = new VertexPositionColor( new XnaV3( (float)v.X, (float)v.Y, 0 ), color.AsXnaColor() );
359  }
360 
361  Int16[] indices = new Int16[cache.Triangles.Length * 3];
362  for ( int i = 0; i < cache.Triangles.Length; i++ )
363  {
364  indices[3 * i] = cache.Triangles[i].i1;
365  indices[3 * i + 1] = cache.Triangles[i].i2;
366  indices[3 * i + 2] = cache.Triangles[i].i3;
367  }
368 
369  device.RasterizerState = RasterizerState.CullCounterClockwise;
370  device.BlendState = blendState;
371 #if WINDOWS_PHONE
372  // The WP7 emulator interprets cullmodes incorectly sometimes.
373  device.RasterizerState = RasterizerState.CullNone;
374 #endif
375 
376  Effect effect = Graphics.GetColorEffect( ref matrix, LightingEnabled );
377  foreach ( EffectPass pass in effect.CurrentTechnique.Passes )
378  {
379  pass.Apply();
380  device.SamplerStates[0] = SamplerState.LinearClamp;
381  device.DrawUserIndexedPrimitives<VertexPositionColor>(
382  PrimitiveType.TriangleList,
383  vertices, 0, vertices.Length,
384  indices, 0, indices.Length / 3
385  );
386  }
387  }
388 
389  public static void DrawPolygon( Vector[] vertices, ref Matrix matrix, Color color )
390  {
391  if ( vertices.Length < 3 )
392  throw new ArgumentException( "Polygon must have at least three vertices" );
393 
394  var device = Game.GraphicsDevice;
395 
396  VertexPositionColor[] colorVertices = new VertexPositionColor[vertices.Length];
397  for ( int i = 0; i < colorVertices.Length; i++ )
398  {
399  Vector p = vertices[i];
400  colorVertices[i] = new VertexPositionColor(
401  new XnaV3( (float)p.X, (float)p.Y, 0 ),
402  color.AsXnaColor()
403  );
404  }
405 
406  int n = colorVertices.Length;
407  Int16[] indices = new Int16[2 * n];
408  for ( int i = 0; i < ( n - 1 ); i++ )
409  {
410  indices[2 * i] = (Int16)i;
411  indices[2 * i + 1] = (Int16)( i + 1 );
412  }
413  indices[2 * ( n - 1 )] = (Int16)( n - 1 );
414  indices[2 * ( n - 1 ) + 1] = (Int16)0;
415 
416  Effect effect = Graphics.GetColorEffect( ref matrix, LightingEnabled );
417  foreach ( EffectPass pass in effect.CurrentTechnique.Passes )
418  {
419  pass.Apply();
420  device.SamplerStates[0] = SamplerState.LinearClamp;
421  device.DrawUserIndexedPrimitives<VertexPositionColor>(
422  PrimitiveType.LineStrip,
423  colorVertices, 0, colorVertices.Length,
424  indices, 0, indices.Length - 1
425  );
426 
427  }
428  }
429 
430 
431  internal static void DrawVertices( Vector[] vertices, Matrix matrix, Color color )
432  {
433  VertexPositionColor[] pointVertices = new VertexPositionColor[vertices.Length];
434  for ( int i = 0; i < pointVertices.Length; i++ )
435  {
436  Vector p = vertices[i];
437  pointVertices[i] = new VertexPositionColor(
438  new XnaV3( (float)p.X, (float)p.Y, 0 ),
439  Color.Red.AsXnaColor()
440  );
441  }
442 
443  var device = Game.GraphicsDevice;
444  //device.RenderState.PointSize = 2;
445 
446  BasicEffect effect = Graphics.BasicColorEffect;
447  effect.World = matrix;
448  foreach ( var pass in effect.CurrentTechnique.Passes )
449  {
450  pass.Apply();
451  device.SamplerStates[0] = SamplerState.LinearClamp;
452  device.DrawUserPrimitives<VertexPositionColor>(
453  PrimitiveType.LineList,
454  pointVertices, 0, pointVertices.Length
455  );
456  }
457  }
458  }
459 }
static void DrawShape(Shape shape, ref Matrix transformation, ref Matrix textureTransformation, Image texture, Vector textureWrapSize, Color color)
Piirtää kuvion niin, että tekstuuri täyttää sen.
Definition: Renderer.cs:285
readonly Vector [] OutlineVertices
Ulkoreunan verteksit, lueteltuna vastapäivään.
Definition: Shapes.cs:586
Kuvio.
Definition: Shapes.cs:48
readonly Vector [] Vertices
Kaikki verteksit, ml. kolmioiden kulmapisteet.
Definition: Shapes.cs:591
Vector Direction
Definition: Shapes.cs:482
static readonly Color Black
Musta.
Definition: Color.cs:494
Microsoft.Xna.Framework.Vector2 XnaV2
Definition: Renderer.cs:37
static readonly Color Red
Punainen.
Definition: Color.cs:804
double Length
Definition: Shapes.cs:483
static void DrawImage(Image texture, ref Matrix matrix, Vector wrapSize)
Definition: Renderer.cs:126
Fontti.
Definition: Font.cs:22
Int16 i1
Kulmapisteet.
Definition: Shapes.cs:552
static void DrawRaySegment(RaySegment segment, ref Matrix matrix, Color color)
Definition: Renderer.cs:308
readonly IndexTriangle [] Triangles
Kolmiot, joiden avulla kuvio voidaan täyttää värillä.
Definition: Shapes.cs:596
Kuva.
Definition: Image.cs:24
static void EndDrawingInsideShape()
Definition: Renderer.cs:257
Sisältää valmiiksi lasketut kolmiot, joiden avulla piirtäminen on suoraviivaista. ...
Definition: Shapes.cs:577
double Y
Definition: Vector.cs:275
Peliluokka reaaliaikaisille peleille.
Definition: DebugScreen.cs:10
double X
Definition: Vector.cs:274
static new GraphicsDevice GraphicsDevice
Definition: Game.cs:368
static bool LightingEnabled
Definition: Renderer.cs:73
Väri.
Definition: Color.cs:13
Vector Origin
Definition: Shapes.cs:481
Luokka, joka sisältää metodeita kuvioiden ja tekstuurien piirtämiseen 2D-tasossa. ...
Definition: Renderer.cs:51
static void DrawShape(Shape shape, ref Matrix matrix, Color color)
Definition: Renderer.cs:292
static void DrawPolygon(Vector[] vertices, ref Matrix matrix, Color color)
Definition: Renderer.cs:389
2D-vektori.
Definition: Vector.cs:56
Microsoft.Xna.Framework.Vector3 XnaV3
Definition: Renderer.cs:38
static readonly Color White
Valkoinen.
Definition: Color.cs:894
static void BeginDrawingInsideShape(Shape shape, ref Matrix transformation)
Makes all the subsequent draw calls until EndDrawingInsideShape limit the drawing inside shape (trans...
Definition: Renderer.cs:239