source: 2013/30/MiskaK/MW2(My Warfare 2)/Paranneltu Jypeli/GameObjects/Surface.cs @ 4507

Revision 4507, 22.0 KB checked in by anlakane, 6 years ago (diff)

Talletus.

Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using Physics2DDotNet.Shapes;
6
7namespace Jypeli
8{
9    /// <summary>
10    /// Tasainen tai epätasainen pinta.
11    /// </summary>
12    public class Surface : PhysicsObject
13    {
14        double[] heights = null;
15        double scale = 1.0;
16
17        #region Constructors
18
19        /// <summary>
20        /// Helppo tapa lisätä kenttään epätasainen pinta.
21        /// Pinta kuvataan luettelemalla Y-koordinaatteja vasemmalta oikealle lukien. Kahden Y-koordinaatin
22        /// väli on aina sama.
23        /// </summary>
24        /// <param name="width">Pinnan leveys</param>
25        /// <param name="heights">Y-koordinaatit lueteltuna vasemmalta oikealle.</param>
26        /// <param name="scale">Vakio, jolla jokainen Y-koordinaatti kerrotaan. Hyödyllinen,
27        /// jos halutaan muuttaa koko pinnan korkeutta muuttamatta jokaista pistettä yksitellen.
28        /// Tavallisesti arvoksi kelpaa 1.0.</param>
29        /// <remarks>
30        /// Huomaa, että pinnassa ei voi olla kahta pistettä päällekkäin.
31        /// </remarks>
32        public Surface( double width, double[] heights, double scale )
33            : this( width, heights, scale, CalculateShapeParams( width, heights ) )
34        {
35        }
36
37        public Surface( double width, double[] heights, double scale, CollisionShapeParameters shapeParameters )
38            : base( width, CalculateHeight( heights, scale ), CreateRuggedShape( width, heights, scale ), shapeParameters )
39        {
40            InitializeRugged( heights, scale );
41        }
42
43        /// <summary>
44        /// Luo tasaisen pinnan.
45        /// </summary>
46        /// <param name="width">Pinnan leveys</param>
47        /// <param name="height">Pinnan korkeus</param>
48        public Surface( double width, double height )
49            : base( width, height, Shape.Rectangle )
50        {
51            InitializeFlat( height );
52        }
53
54        public Surface( double width, double height, CollisionShapeParameters shapeParameters )
55            : base( width, height, Shape.Rectangle, shapeParameters )
56        {
57            InitializeFlat( height );
58        }
59
60        private void InitializeFlat( double height )
61        {
62            this.heights = new double[1] { height };
63            this.scale = 1.0;
64
65            this.Color = Color.ForestGreen;
66            this.TextureFillsShape = true;
67            MakeStatic();
68        }
69
70        private void InitializeRugged( double[] heights, double scale )
71        {
72            this.heights = heights;
73            this.scale = scale;
74            this.Color = Color.ForestGreen;
75            this.TextureFillsShape = true;
76            MakeStatic();
77        }
78
79        /// <summary>
80        /// Luo satunnaisen pinnan.
81        /// </summary>
82        /// <param name="width">Pinnan leveys</param>
83        /// <param name="min">Matalin kohta.</param>
84        /// <param name="max">Korkein kohta.</param>
85        /// <param name="points">Pisteiden määrä.</param>
86        /// <returns></returns>
87        public Surface( double width, double min, double max, int points )
88            : this( width, RandomGen.NextDoubleArray( Math.Max( min, 1.0f ), max, points ), 1.0 )
89        {
90        }
91
92        public Surface( double width, double min, double max, int points, CollisionShapeParameters shapeParameters )
93            : this( width, RandomGen.NextDoubleArray( Math.Max( min, 1.0f ), max, points ), 1.0, shapeParameters )
94        {
95        }
96       
97        /// <summary>
98        /// Luo satunnaisen pinnan.
99        /// </summary>
100        /// <param name="width">Pinnan leveys</param>
101        /// <param name="min">Matalin kohta.</param>
102        /// <param name="max">Korkein kohta.</param>
103        /// <param name="points">Pisteiden määrä.</param>
104        /// <param name="maxchange">Suurin sallittu erotus kahden pisteen välillä.</param>
105        /// <returns></returns>
106        public Surface( double width, double min, double max, int points, int maxchange )
107            : this( width, RandomGen.NextDoubleArray( Math.Max( min, 1.0f ), max, points, maxchange ), 1.0 )
108        {
109        }
110
111        public Surface( double width, double min, double max, int points, int maxchange, CollisionShapeParameters shapeParameters )
112            : this( width, RandomGen.NextDoubleArray( Math.Max(min, 1.0f), max, points, maxchange ), 1.0, shapeParameters )
113        {
114        }
115
116        #endregion
117
118        #region Static private methods for constructors
119
120        private static double CalculateHeight( double[] heights, double scale )
121        {
122            if ( heights.Length < 2 )
123                throw new Exception( "At least two Y-points needed in order to create ground" );
124            if ( heights.Min() < 1 )
125                throw new Exception( "The heights must be positive and at least 1" ); // JN: doesn't work well with 0 values (slow and memory usage problems)
126
127            return heights.Max() * scale;
128        }
129       
130        private static Vector[] CalculateVertexes( double width, double[] heights, double scale )
131        {
132            int n = heights.Length;
133            double step = width / ( n - 1 );
134            double maxHeight = heights.Max() * scale;
135
136            // Each point should be adjusted by this amount in order to get the center of the shape at (0, 0)
137            // This way, drawing a single texture on top of it covers the whole shape.
138            double halfHeight = maxHeight / 2;
139
140            Vector[] vertexes = new Vector[n * 2];
141
142            // Let's start from the bottom right corner and head clockwise from there.
143            Vector bottomRight = new Vector( width / 2, -halfHeight );
144
145            // Bottom vertexes, right to left
146            for ( int i = 0; i < n; i++ )
147            {
148                vertexes[i] = new Vector( bottomRight.X - ( i * step ), bottomRight.Y );
149            }
150
151            double left = -width / 2;
152
153            // Top vertexes, left to right
154            for ( int i = 0; i < n; i++ )
155            {
156                vertexes[n + i] = new Vector( left + ( i * step ), heights[i] * scale - halfHeight );
157            }
158
159            return vertexes;
160        }
161
162        private static Polygon CreateRuggedShape( double width, double[] heights, double scale )
163        {
164            Vector[] vertexes = CalculateVertexes( width, heights, scale );
165            return CreateShape( width, heights, vertexes );
166        }
167       
168        private static Polygon CreateShape( double width, double[] heights, Vector[] vertexes )
169        {
170            int n = heights.Length;
171            double step = width / ( n - 1 );
172            IndexTriangle[] triangles = new IndexTriangle[( n - 1 ) * 2];
173            Int16[] outlineIndices = new Int16[n * 2];
174
175            for ( int i = 0; i < n * 2; i++ )
176                outlineIndices[i] = (Int16)i;
177            for ( int i = 0; i < n - 1; i++ )
178            {
179                triangles[2 * i] = new IndexTriangle( i, 2 * n - i - 2, 2 * n - i - 1 );
180                triangles[2 * i + 1] = new IndexTriangle( i, i + 1, 2 * n - i - 2 );
181            }
182
183            Vector[] outlineVertices = new Vector[outlineIndices.Length];
184            for ( int i = 0; i < outlineIndices.Length; i++ )
185                outlineVertices[i] = vertexes[outlineIndices[i]];
186
187            return new Polygon( new ShapeCache( vertexes, triangles, outlineIndices ), false );
188        }
189
190        private static double GetMinHeightDifference( double[] heights )
191        {
192            int n = heights.Length;
193            double minHeightDifference = double.PositiveInfinity;
194
195            for ( int i = 0; i < n - 1; i++ )
196            {
197                double diff = Math.Abs( heights[i + 1] - heights[i] );
198                if ( diff > double.Epsilon && diff < minHeightDifference ) minHeightDifference = diff;
199            }
200
201            return minHeightDifference;
202        }
203
204        private static CollisionShapeParameters CalculateShapeParams( double width, double[] heights )
205        {
206            int n = heights.Length;
207            double step = width / ( n - 1 );
208
209            CollisionShapeParameters parms;
210            parms.DistanceGridSpacing = Math.Max( step / 8, GetMinHeightDifference( heights ) / 2 );
211            parms.MaxVertexDistance = 20;
212            return parms;
213        }
214
215        #endregion
216
217        #region Factory methods
218
219        /// <summary>
220        /// Luo kentälle tasaisen reunan.
221        /// Ei lisää reunaa automaattisesti kenttään.
222        /// </summary>
223        /// <param name="level">Kenttä</param>
224        /// <param name="direction">Mikä reuna luodaan.</param>
225        /// <returns>Reunaolio</returns>
226        public static Surface Create( Level level, Direction direction )
227        {
228            if ( direction == Direction.Left ) return CreateLeft( level );
229            if ( direction == Direction.Right ) return CreateRight( level );
230            if ( direction == Direction.Up ) return CreateTop( level );
231            if ( direction == Direction.Down ) return CreateBottom( level );
232
233            return null;
234        }
235
236        /// <summary>
237        /// Luo kentälle epätasaisen reunan.
238        /// Ei lisää reunaa automaattisesti kenttään.
239        /// </summary>
240        /// <param name="level">Kenttä</param>
241        /// <param name="min">Matalin kohta.</param>
242        /// <param name="max">Korkein kohta.</param>
243        /// <param name="points">Pisteiden määrä.</param>
244        /// <param name="direction">Mikä reuna luodaan.</param>
245        /// <returns>Reunaolio</returns>
246        public static Surface Create( Level level, Direction direction, double min, double max, int points )
247        {
248            if ( direction == Direction.Left ) return CreateLeft( level, min, max, points );
249            if ( direction == Direction.Right ) return CreateRight( level, min, max, points );
250            if ( direction == Direction.Up ) return CreateTop( level, min, max, points );
251            if ( direction == Direction.Down ) return CreateBottom( level, min, max, points );
252
253            return null;
254        }
255
256        /// <summary>
257        /// Luo kentälle epätasaisen reunan.
258        /// Ei lisää reunaa automaattisesti kenttään.
259        /// </summary>
260        /// <param name="level">Kenttä</param>
261        /// <param name="min">Matalin kohta.</param>
262        /// <param name="max">Korkein kohta.</param>
263        /// <param name="points">Pisteiden määrä.</param>
264        /// <param name="maxchange">Suurin sallittu erotus kahden pisteen välillä.</param>
265        /// <param name="direction">Mikä reuna luodaan.</param>
266        /// <returns>Reunaolio</returns>
267        public static Surface Create( Level level, Direction direction, double min, double max, int points, int maxchange )
268        {
269            if ( direction == Direction.Left ) return CreateLeft( level, min, max, points, maxchange );
270            if ( direction == Direction.Right ) return CreateRight( level, min, max, points, maxchange );
271            if ( direction == Direction.Up ) return CreateTop( level, min, max, points, maxchange );
272            if ( direction == Direction.Down ) return CreateBottom( level, min, max, points, maxchange );
273
274            return null;
275        }
276
277        /// <summary>
278        /// Luo kentälle tasaisen vasemman reunan.
279        /// Ei lisää reunaa automaattisesti kenttään.
280        /// </summary>
281        /// <param name="level">Kenttä</param>
282        /// <returns>Reunaolio</returns>
283        public static Surface CreateLeft( Level level )
284        {
285            double thickness = level.GetBorderThickness();
286            Surface ground = new Surface( level.Height, thickness );
287            ground.Angle = -Angle.RightAngle;
288            ground.Position = new Vector( level.Left - ( thickness / 2 ), level.Center.Y );
289            return ground;
290        }
291
292        /// <summary>
293        /// Luo kentälle epätasaisen vasemman reunan.
294        /// Ei lisää reunaa automaattisesti kenttään.
295        /// </summary>
296        /// <param name="level">Kenttä</param>
297        /// <param name="min">Matalin kohta.</param>
298        /// <param name="max">Korkein kohta.</param>
299        /// <param name="points">Pisteiden määrä.</param>
300        /// <returns>Reunaolio</returns>
301        public static Surface CreateLeft( Level level, double min, double max, int points )
302        {
303            Surface ground = new Surface( level.Height + 2 * max, min, max, points );
304            ground.Angle = -Angle.RightAngle;
305            ground.Position = new Vector( level.Left - ( max / 2 ), level.Center.Y );
306            return ground;
307        }
308
309        /// <summary>
310        /// Luo kentälle epätasaisen vasemman reunan.
311        /// Ei lisää reunaa automaattisesti kenttään.
312        /// </summary>
313        /// <param name="level">Kenttä</param>
314        /// <param name="min">Matalin kohta.</param>
315        /// <param name="max">Korkein kohta.</param>
316        /// <param name="points">Pisteiden määrä.</param>
317        /// <param name="maxchange">Suurin sallittu erotus kahden pisteen välillä.</param>
318        /// <returns>Reunaolio</returns>
319        public static Surface CreateLeft( Level level, double min, double max, int points, int maxchange )
320        {
321            Surface ground = new Surface( level.Height + 2 * max, min, max, points, maxchange );
322            ground.Angle = -Angle.RightAngle;
323            ground.Position = new Vector( level.Left - ( max / 2 ), level.Center.Y );
324            return ground;
325        }
326
327        /// <summary>
328        /// Luo kentälle tasaisen oikean reunan.
329        /// Ei lisää reunaa automaattisesti kenttään.
330        /// </summary>
331        /// <param name="level">Kenttä</param>
332        /// <returns>Reunaolio</returns>
333        public static Surface CreateRight( Level level )
334        {
335            double thickness = level.GetBorderThickness();
336            Surface ground = new Surface( level.Height, thickness );
337            ground.Angle = Angle.RightAngle;
338            ground.Position = new Vector( level.Right + ( thickness / 2 ), level.Center.Y );
339            return ground;
340        }
341
342        /// <summary>
343        /// Luo kentälle epätasaisen oikean reunan.
344        /// Ei lisää reunaa automaattisesti kenttään.
345        /// </summary>
346        /// <param name="min">Matalin kohta.</param>
347        /// <param name="max">Korkein kohta.</param>
348        /// <param name="points">Pisteiden määrä.</param>
349        /// <param name="level">Kenttä</param>
350        /// <returns>Reunaolio</returns>
351        public static Surface CreateRight( Level level, double min, double max, int points )
352        {
353            Surface ground = new Surface( level.Height + 2 * max, min, max, points );
354            ground.Angle = Angle.RightAngle;
355            ground.Position = new Vector( level.Right + ( max / 2 ), level.Center.Y );
356            return ground;
357        }
358
359        /// <summary>
360        /// Luo kentälle epätasaisen oikean reunan.
361        /// Ei lisää reunaa automaattisesti kenttään.
362        /// </summary>
363        /// <param name="min">Matalin kohta.</param>
364        /// <param name="max">Korkein kohta.</param>
365        /// <param name="points">Pisteiden määrä.</param>
366        /// <param name="level">Kenttä</param>
367        /// <param name="maxchange">Suurin sallittu erotus kahden pisteen välillä.</param>
368        /// <returns>Reunaolio</returns>
369        public static Surface CreateRight( Level level, double min, double max, int points, int maxchange )
370        {
371            Surface ground = new Surface( level.Height + 2 * max, min, max, points, maxchange );
372            ground.Angle = Angle.RightAngle;
373            ground.Position = new Vector( level.Right + ( max / 2 ), level.Center.Y );
374            return ground;
375        }
376
377        /// <summary>
378        /// Luo kentälle tasaisen yläreunan.
379        /// Ei lisää reunaa automaattisesti kenttään.
380        /// </summary>
381        /// <param name="level">Kenttä</param>
382        /// <returns>Reunaolio</returns>
383        public static Surface CreateTop( Level level )
384        {
385            double thickness = level.GetBorderThickness();
386            Surface ground = new Surface( level.Width + ( 2 * thickness ), thickness );
387            ground.Angle = Angle.StraightAngle;
388            ground.Position = new Vector( level.Center.X, level.Top + ( thickness / 2 ) );
389            return ground;
390        }
391
392        /// <summary>
393        /// Luo kentälle epätasaisen yläreunan.
394        /// Ei lisää reunaa automaattisesti kenttään.
395        /// </summary>
396        /// <param name="level">Kenttä</param>
397        /// <param name="min">Matalin kohta.</param>
398        /// <param name="max">Korkein kohta.</param>
399        /// <param name="points">Pisteiden määrä.</param>
400        /// <returns>Reunaolio</returns>
401        public static Surface CreateTop( Level level, double min, double max, int points )
402        {
403            Surface ground = new Surface( level.Width + 2 * max, min, max, points );
404            ground.Angle = Angle.StraightAngle;
405            ground.Position = new Vector( level.Center.X, level.Top + ( max / 2 ) );
406            return ground;
407        }
408
409        /// <summary>
410        /// Luo kentälle epätasaisen yläreunan.
411        /// Ei lisää reunaa automaattisesti kenttään.
412        /// </summary>
413        /// <param name="level">Kenttä</param>
414        /// <param name="min">Matalin kohta.</param>
415        /// <param name="max">Korkein kohta.</param>
416        /// <param name="points">Pisteiden määrä.</param>
417        /// <param name="maxchange">Suurin sallittu erotus kahden pisteen välillä.</param>
418        /// <returns>Reunaolio</returns>
419        public static Surface CreateTop( Level level, double min, double max, int points, int maxchange )
420        {
421            Surface ground = new Surface( level.Width + 2 * max, min, max, points, maxchange );
422            ground.Angle = Angle.StraightAngle;
423            ground.Position = new Vector( level.Center.X, level.Top + ( max / 2 ) );
424            return ground;
425        }
426
427        /// <summary>
428        /// Luo kentälle tasaisen alareunan.
429        /// Ei lisää reunaa automaattisesti kenttään.
430        /// </summary>
431        /// <param name="level">Kenttä</param>
432        /// <returns>Reunaolio</returns>
433        public static Surface CreateBottom( Level level )
434        {
435            double thickness = level.GetBorderThickness();
436            Surface ground = new Surface( level.Width + ( 2 * thickness ), thickness );
437            ground.Position = new Vector( level.Center.X, level.Bottom - ( thickness / 2 ) );
438            return ground;
439        }
440
441        /// <summary>
442        /// Luo kentälle epätasaisen alareunan.
443        /// Ei lisää reunaa automaattisesti kenttään.
444        /// </summary>
445        /// <param name="level">Kenttä</param>
446        /// <param name="min">Matalin kohta.</param>
447        /// <param name="max">Korkein kohta.</param>
448        /// <param name="points">Pisteiden määrä.</param>
449        /// <returns>Reunaolio</returns>
450        public static Surface CreateBottom( Level level, double min, double max, int points )
451        {
452            Surface ground = new Surface( level.Width + 2 * max, min, max, points );
453            ground.Position = new Vector( level.Center.X, level.Bottom - ( max / 2 ) );
454            return ground;
455        }
456
457        /// <summary>
458        /// Luo kentälle epätasaisen alareunan.
459        /// Ei lisää reunaa automaattisesti kenttään.
460        /// </summary>
461        /// <param name="level">Kenttä</param>
462        /// <param name="min">Matalin kohta.</param>
463        /// <param name="max">Korkein kohta.</param>
464        /// <param name="points">Pisteiden määrä.</param>
465        /// <param name="maxchange">Suurin sallittu erotus kahden pisteen välillä.</param>
466        /// <returns>Reunaolio</returns>
467        public static Surface CreateBottom( Level level, double min, double max, int points, int maxchange )
468        {
469            Surface ground = new Surface( level.Width + 2 * max, min, max, points, maxchange );
470            ground.Position = new Vector( level.Center.X, level.Bottom - ( max / 2 ) );
471            return ground;
472        }
473
474        #endregion
475
476        #region Public methods
477
478        public double GetGroundHeight( double x )
479        {
480            if ( heights == null || x < Left || x > Right ) return Top;
481
482            int n = heights.Length;
483            double step = Width / ( n - 1 );
484            double maxHeight = heights.Max() * scale;
485
486            double indexX = ( Width / 2 + x ) / step;
487            int lowerIndex = (int)Math.Floor( indexX );
488            int upperIndex = (int)Math.Ceiling( indexX );
489
490            if ( upperIndex >= n ) return Top; // DEBUG
491            if ( lowerIndex == upperIndex ) return Bottom + scale * heights[lowerIndex];
492
493            double k = ( heights[upperIndex] - heights[lowerIndex] ) / step;
494            double relX = ( Width / 2 + x ) % step;
495
496            return Bottom + heights[lowerIndex] + relX * k;
497        }
498
499        public Vector GetGroundNormal( double x )
500        {
501            if ( heights == null || x < Left || x > Right ) return Vector.UnitY;
502
503            int n = heights.Length;
504            double step = Width / ( n - 1 );
505            double maxHeight = heights.Max() * scale;
506
507            double indexX = ( Width / 2 + x ) / step;
508            int lowerIndex = (int)Math.Floor( indexX );
509            int upperIndex = (int)Math.Ceiling( indexX );
510
511            if ( upperIndex >= n ) return Vector.UnitY; // DEBUG
512            if ( lowerIndex == upperIndex )
513            {
514                return ( GetGroundNormal( x - step / 2 ) + GetGroundNormal( x + step / 2 ) ) / 2;
515            }
516
517            double k = ( heights[upperIndex] - heights[lowerIndex] ) / step;
518            return Vector.FromLengthAndAngle( 1, Angle.ArcTan( k ) + Angle.RightAngle );
519        }
520
521        #endregion
522    }
523}
Note: See TracBrowser for help on using the repository browser.