1 // MIT License
2 
3 // Copyright (c) 2019 Erin Catto
4 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 
23 #include "settings.h"
24 #include "test.h"
25 
26 class EdgeShapesCallback : public b2RayCastCallback
27 {
28 public:
EdgeShapesCallback()29 	EdgeShapesCallback()
30 	{
31 		m_fixture = NULL;
32 	}
33 
ReportFixture(b2Fixture * fixture,const b2Vec2 & point,const b2Vec2 & normal,float fraction)34 	float ReportFixture(b2Fixture* fixture, const b2Vec2& point,
35 						  const b2Vec2& normal, float fraction) override
36 	{
37 		m_fixture = fixture;
38 		m_point = point;
39 		m_normal = normal;
40 
41 		return fraction;
42 	}
43 
44 	b2Fixture* m_fixture;
45 	b2Vec2 m_point;
46 	b2Vec2 m_normal;
47 };
48 
49 class EdgeShapes : public Test
50 {
51 public:
52 
53 	enum
54 	{
55 		e_maxBodies = 256
56 	};
57 
EdgeShapes()58 	EdgeShapes()
59 	{
60 		// Ground body
61 		{
62 			b2BodyDef bd;
63 			b2Body* ground = m_world->CreateBody(&bd);
64 
65 			float x1 = -20.0f;
66 			float y1 = 2.0f * cosf(x1 / 10.0f * b2_pi);
67 			for (int32 i = 0; i < 80; ++i)
68 			{
69 				float x2 = x1 + 0.5f;
70 				float y2 = 2.0f * cosf(x2 / 10.0f * b2_pi);
71 
72 				b2EdgeShape shape;
73 				shape.SetTwoSided(b2Vec2(x1, y1), b2Vec2(x2, y2));
74 				ground->CreateFixture(&shape, 0.0f);
75 
76 				x1 = x2;
77 				y1 = y2;
78 			}
79 		}
80 
81 		{
82 		b2Vec2 vertices[3];
83 		vertices[0].Set(-0.5f, 0.0f);
84 		vertices[1].Set(0.5f, 0.0f);
85 		vertices[2].Set(0.0f, 1.5f);
86 		m_polygons[0].Set(vertices, 3);
87 	}
88 
89 		{
90 			b2Vec2 vertices[3];
91 			vertices[0].Set(-0.1f, 0.0f);
92 			vertices[1].Set(0.1f, 0.0f);
93 			vertices[2].Set(0.0f, 1.5f);
94 			m_polygons[1].Set(vertices, 3);
95 		}
96 
97 		{
98 			float w = 1.0f;
99 			float b = w / (2.0f + b2Sqrt(2.0f));
100 			float s = b2Sqrt(2.0f) * b;
101 
102 			b2Vec2 vertices[8];
103 			vertices[0].Set(0.5f * s, 0.0f);
104 			vertices[1].Set(0.5f * w, b);
105 			vertices[2].Set(0.5f * w, b + s);
106 			vertices[3].Set(0.5f * s, w);
107 			vertices[4].Set(-0.5f * s, w);
108 			vertices[5].Set(-0.5f * w, b + s);
109 			vertices[6].Set(-0.5f * w, b);
110 			vertices[7].Set(-0.5f * s, 0.0f);
111 
112 			m_polygons[2].Set(vertices, 8);
113 		}
114 
115 		{
116 			m_polygons[3].SetAsBox(0.5f, 0.5f);
117 		}
118 
119 		{
120 			m_circle.m_radius = 0.5f;
121 		}
122 
123 		m_bodyIndex = 0;
124 		memset(m_bodies, 0, sizeof(m_bodies));
125 
126 		m_angle = 0.0f;
127 	}
128 
Create(int32 index)129 	void Create(int32 index)
130 	{
131 		if (m_bodies[m_bodyIndex] != NULL)
132 		{
133 			m_world->DestroyBody(m_bodies[m_bodyIndex]);
134 			m_bodies[m_bodyIndex] = NULL;
135 		}
136 
137 		b2BodyDef bd;
138 
139 		float x = RandomFloat(-10.0f, 10.0f);
140 		float y = RandomFloat(10.0f, 20.0f);
141 		bd.position.Set(x, y);
142 		bd.angle = RandomFloat(-b2_pi, b2_pi);
143 		bd.type = b2_dynamicBody;
144 
145 		if (index == 4)
146 		{
147 			bd.angularDamping = 0.02f;
148 		}
149 
150 		m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
151 
152 		if (index < 4)
153 		{
154 			b2FixtureDef fd;
155 			fd.shape = m_polygons + index;
156 			fd.friction = 0.3f;
157 			fd.density = 20.0f;
158 			m_bodies[m_bodyIndex]->CreateFixture(&fd);
159 		}
160 		else
161 		{
162 			b2FixtureDef fd;
163 			fd.shape = &m_circle;
164 			fd.friction = 0.3f;
165 			fd.density = 20.0f;
166 			m_bodies[m_bodyIndex]->CreateFixture(&fd);
167 		}
168 
169 		m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
170 	}
171 
DestroyBody()172 	void DestroyBody()
173 	{
174 		for (int32 i = 0; i < e_maxBodies; ++i)
175 		{
176 			if (m_bodies[i] != NULL)
177 			{
178 				m_world->DestroyBody(m_bodies[i]);
179 				m_bodies[i] = NULL;
180 				return;
181 			}
182 		}
183 	}
184 
Keyboard(int key)185 	void Keyboard(int key) override
186 	{
187 		switch (key)
188 		{
189 		case GLFW_KEY_1:
190 		case GLFW_KEY_2:
191 		case GLFW_KEY_3:
192 		case GLFW_KEY_4:
193 		case GLFW_KEY_5:
194 			Create(key - GLFW_KEY_1);
195 			break;
196 
197 		case GLFW_KEY_D:
198 			DestroyBody();
199 			break;
200 		}
201 	}
202 
Step(Settings & settings)203 	void Step(Settings& settings) override
204 	{
205 		bool advanceRay = settings.m_pause == 0 || settings.m_singleStep;
206 
207 		Test::Step(settings);
208 		g_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
209 		m_textLine += m_textIncrement;
210 
211 		float L = 25.0f;
212 		b2Vec2 point1(0.0f, 10.0f);
213 		b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle)));
214 		b2Vec2 point2 = point1 + d;
215 
216 		EdgeShapesCallback callback;
217 
218 		m_world->RayCast(&callback, point1, point2);
219 
220 		if (callback.m_fixture)
221 		{
222 			g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
223 
224 			g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
225 
226 			b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
227 			g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
228 		}
229 		else
230 		{
231 			g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
232 		}
233 
234 		if (advanceRay)
235 		{
236 			m_angle += 0.25f * b2_pi / 180.0f;
237 		}
238 	}
239 
Create()240 	static Test* Create()
241 	{
242 		return new EdgeShapes;
243 	}
244 
245 	int32 m_bodyIndex;
246 	b2Body* m_bodies[e_maxBodies];
247 	b2PolygonShape m_polygons[4];
248 	b2CircleShape m_circle;
249 
250 	float m_angle;
251 };
252 
253 static int testIndex = RegisterTest("Geometry", "Edge Shapes", EdgeShapes::Create);
254