2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <chipmunk/chipmunk.h>
18 #include <dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.h>
19 #include <dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.h>
23 GLuint LoadShader(GLenum shaderType, const char* shaderSource)
25 GLuint shader = glCreateShader(shaderType);
28 glShaderSource(shader, 1, &shaderSource, NULL);
29 glCompileShader(shader);
31 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
32 if(compiled != GL_TRUE)
35 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
39 std::vector<char> logBuffer;
40 logBuffer.resize(infoLen + 1);
41 glGetShaderInfoLog(shader, infoLen, NULL, &logBuffer[0]);
42 fprintf(stderr, "%s\n", &logBuffer[0]);
45 glDeleteShader(shader);
53 GLuint CreateProgram(const char* vertexSource, const char* fragmentSource)
55 GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
60 GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
65 GLuint program = glCreateProgram();
68 glAttachShader(program, vertexShader);
69 glAttachShader(program, fragmentShader);
70 glLinkProgram(program);
71 GLint linkStatus = GL_FALSE;
72 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
73 if(linkStatus != GL_TRUE)
76 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
79 std::vector<char> logBuffer;
80 logBuffer.resize(bufLength + 1);
81 glGetProgramInfoLog(program, bufLength, NULL, &logBuffer[0]);
82 fprintf(stderr, "%s\n", &logBuffer[0]);
85 glDeleteProgram(program);
93 namespace Dali::Toolkit::Physics::Internal
95 static void DebugDrawCircleImpl(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data)
97 auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
98 debugRenderer->DrawCircle(pos, angle, radius, outlineColor, fillColor);
101 static void DebugDrawSegmentImpl(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data)
103 auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
104 debugRenderer->DrawSegment(a, b, color);
107 void DebugDrawFatSegmentImpl(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data)
109 auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
110 debugRenderer->DrawFatSegment(a, b, radius, outlineColor, fillColor);
113 void DebugDrawPolygonImpl(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data)
115 auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
116 debugRenderer->DrawPolygon(count, verts, radius, outlineColor, fillColor);
119 void DebugDrawDotImpl(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data)
121 auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
122 debugRenderer->DrawDot(size, pos, color);
125 cpSpaceDebugColor DebugDrawColorForShapeImpl(cpShape* shape, cpDataPointer data)
127 auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
128 return debugRenderer->DrawColorForShape(shape);
131 std::unique_ptr<PhysicsDebugRenderer> PhysicsDebugRenderer::New(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor)
133 auto renderer = std::make_unique<PhysicsDebugRenderer>(width, height, camera, adaptor);
135 renderer->mRenderCallback = Dali::RenderCallback::New<PhysicsDebugRenderer>(renderer.get(), &PhysicsDebugRenderer::OnRender);
139 PhysicsDebugRenderer::PhysicsDebugRenderer(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor)
144 mPositionLocation(-1),
147 mFillColourLocation(-1),
148 mOutlineColourLocation(-1),
149 mProjectionLocation(-1),
150 mModelViewLocation(-1),
155 mDebugDrawOptions.drawCircle = DebugDrawCircleImpl;
156 mDebugDrawOptions.drawSegment = DebugDrawSegmentImpl;
157 mDebugDrawOptions.drawFatSegment = DebugDrawFatSegmentImpl;
158 mDebugDrawOptions.drawPolygon = DebugDrawPolygonImpl;
159 mDebugDrawOptions.drawDot = DebugDrawDotImpl;
161 mDebugDrawOptions.flags = static_cast<cpSpaceDebugDrawFlags>(CP_SPACE_DEBUG_DRAW_SHAPES |
162 CP_SPACE_DEBUG_DRAW_COLLISION_POINTS |
163 CP_SPACE_DEBUG_DRAW_CONSTRAINTS);
164 mDebugDrawOptions.colorForShape = DebugDrawColorForShapeImpl;
165 mDebugDrawOptions.shapeOutlineColor = cpSpaceDebugColor{0.0f, 1.0f, 1.0f, 0.9f};
166 mDebugDrawOptions.collisionPointColor = cpSpaceDebugColor{1.0f, 0.0f, 0.0f, 1.0f};
167 mDebugDrawOptions.data = this;
170 bool PhysicsDebugRenderer::OnRender(const Dali::RenderCallbackInput& input)
172 if(mState == State::INIT)
175 mState = State::RENDER;
177 glViewport(0, 0, mWidth, mHeight);
184 // Run on first invocation of callback
185 void PhysicsDebugRenderer::Setup()
188 mPositionLocation = glGetAttribLocation(mProgramId, "position");
189 mUvsLocation = glGetAttribLocation(mProgramId, "uvs");
190 mRadiusLocation = glGetAttribLocation(mProgramId, "radius");
191 mFillColourLocation = glGetAttribLocation(mProgramId, "fillColor");
192 mOutlineColourLocation = glGetAttribLocation(mProgramId, "outlineColor");
194 mProjectionLocation = glGetUniformLocation(mProgramId, "projection");
195 mModelViewLocation = glGetUniformLocation(mProgramId, "modelView");
197 glEnable(GL_DEPTH_TEST);
198 glViewport(0, 0, mWidth, mHeight);
200 glGenBuffers(1, &mIndexBufferId);
201 glGenBuffers(1, &mVertexBufferId);
204 void PhysicsDebugRenderer::UpdateWindowSize(Dali::Vector2 size)
207 mHeight = size.height;
210 void PhysicsDebugRenderer::PrepareShader()
212 static const char glVertexShader[] =
214 "in vec2 position;\n"
217 "in vec4 fillColor;\n"
218 "in vec4 outlineColor;\n"
221 "out vec4 v_outline;\n"
222 "uniform mat4 projection;\n"
223 "uniform mat4 modelView;\n"
226 " gl_Position = projection * modelView * vec4(position.xy+radius*uvs, 0.0, 1.0);\n"
228 " v_fill = fillColor;\n"
229 " v_fill.rgb *= v_fill.a;\n"
230 " v_outline = outlineColor;\n"
231 " v_outline.a *= v_outline.a;\n"
234 static const char glFragmentShader[] =
236 "precision mediump float;\n"
239 "in vec4 v_outline;\n"
240 "out vec4 fragColor;\n"
243 " float len=length(v_uvs);\n"
244 " float fw = length(vec2(dFdx(len), dFdy(len)));\n"
245 " float mask=smoothstep(-1.0, fw-1.0, -len);\n"
246 " float outline=1.0-fw;\n"
247 " float outline_mask=smoothstep(outline-fw, outline, len);\n"
248 " vec4 color = v_fill + (v_outline - v_fill*v_outline.a)*outline_mask;\n"
249 " fragColor = color*mask;\n"
252 mProgramId = CreateProgram(glVertexShader, glFragmentShader);
255 void PhysicsDebugRenderer::RenderLines(const Dali::RenderCallbackInput& input)
257 mModelViewMatrix.SetIdentity();
258 mProjectionMatrix = input.projection;
260 Matrix::Multiply(mModelViewMatrix, mModelViewMatrix, input.view);
261 glUseProgram(mProgramId);
263 // In theory, input.clippingBox should tell us the actor position in clip-space.
264 // But, it appears to be bugged.
266 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
268 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferId);
269 glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndices.size() * sizeof(uint16_t), &mIndices[0], GL_STATIC_DRAW);
270 glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferId);
271 glBufferData(GL_ARRAY_BUFFER, mVertices.size() * sizeof(Vertex), &mVertices[0], GL_STATIC_DRAW);
273 GLint stride = 52; // 4*(2 + 2 + 1 + 4 + 4) = 4*13=52
274 glVertexAttribPointer(mPositionLocation, 2, GL_FLOAT, GL_FALSE, stride, 0);
275 glEnableVertexAttribArray(mPositionLocation);
277 glVertexAttribPointer(mUvsLocation, 2, GL_FLOAT, GL_FALSE, stride, (const void*)8);
278 glEnableVertexAttribArray(mUvsLocation);
280 glVertexAttribPointer(mRadiusLocation, 1, GL_FLOAT, GL_FALSE, stride, (const void*)16);
281 glEnableVertexAttribArray(mRadiusLocation);
283 glVertexAttribPointer(mFillColourLocation, 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<const void*>(20));
284 glEnableVertexAttribArray(mFillColourLocation);
285 glVertexAttribPointer(mOutlineColourLocation, 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<const void*>(36));
286 glEnableVertexAttribArray(mOutlineColourLocation);
288 glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix.AsFloat());
289 glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix.AsFloat());
291 glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_SHORT, 0);
296 PhysicsDebugRenderer::Vertex* PhysicsDebugRenderer::PushVertices(uint32_t vertexCount, uint32_t indexCount, const uint16_t* indices)
298 auto base = mVertices.size();
299 mVertices.resize(mVertices.size() + vertexCount);
300 mIndices.reserve(mIndices.size() + indexCount);
301 for(uint32_t i = 0; i < indexCount; ++i)
303 mIndices.push_back(base + indices[i]);
306 return &mVertices[base];
309 PhysicsDebugRenderer::Vertex PhysicsDebugRenderer::MakeVertex(cpVect pos, float u, float v, float r, Vector4 fill, Vector4 outline)
311 auto daliPos = mAdaptor.TranslateFromPhysicsSpace(Vector3((float)pos.x, (float)pos.y, 0.0f));
312 return Vertex{Vector2(daliPos.x, daliPos.y), Vector2(u, v), r, fill, outline};
315 void PhysicsDebugRenderer::DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
317 float r = (float)radius + mPointLineScale;
318 Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
319 Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
320 static const uint16_t indices[] = {0, 1, 2, 0, 2, 3};
322 Vertex* vertices = PushVertices(4, 6, indices);
324 vertices[0] = MakeVertex(pos, -1, -1, r, fill, outline);
325 vertices[1] = MakeVertex(pos, -1, 1, r, fill, outline);
326 vertices[2] = MakeVertex(pos, 1, 1, r, fill, outline);
327 vertices[3] = MakeVertex(pos, 1, -1, r, fill, outline);
329 DrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), 0.75f * radius)), outlineColor);
332 void PhysicsDebugRenderer::DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color)
334 DrawFatSegment(a, b, 0.0f, color, color);
337 void PhysicsDebugRenderer::DrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
339 static const uint16_t indices[] = {0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7};
340 Vertex* vertices = PushVertices(8, 18, indices);
342 cpVect t = cpvnormalize(cpvsub(b, a));
344 float r = (float)radius * mPointLineScale;
345 Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
346 Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
348 vertices[0] = MakeVertex(a, (-t.x + t.y), (-t.x - t.y), r, fill, outline);
349 vertices[1] = MakeVertex(a, (-t.x - t.y), (+t.x - t.y), r, fill, outline);
350 vertices[2] = MakeVertex(a, (-0.0 + t.y), (-t.x + 0.0), r, fill, outline);
351 vertices[3] = MakeVertex(a, (-0.0 - t.y), (+t.x + 0.0), r, fill, outline);
352 vertices[4] = MakeVertex(a, (+0.0 + t.y), (-t.x - 0.0), r, fill, outline);
353 vertices[5] = MakeVertex(a, (+0.0 - t.y), (+t.x - 0.0), r, fill, outline);
354 vertices[6] = MakeVertex(a, (+t.x + t.y), (-t.x + t.y), r, fill, outline);
355 vertices[7] = MakeVertex(a, (+t.x - t.y), (+t.x + t.y), r, fill, outline);
358 void PhysicsDebugRenderer::DrawPolygon(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
360 Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
361 Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
363 std::vector<uint16_t> indices;
364 for(int i = 0; i < count - 2; i++)
366 indices.push_back(0);
367 indices.push_back(4 * (i + 1));
368 indices.push_back(4 * (i + 2));
371 // Polygon outline triangles.
372 for(int i0 = 0; i0 < count; i0++)
374 int i1 = (i0 + 1) % count;
375 indices.push_back(4 * i0 + 0);
376 indices.push_back(4 * i0 + 1);
377 indices.push_back(4 * i0 + 2);
378 indices.push_back(4 * i0 + 0);
379 indices.push_back(4 * i0 + 2);
380 indices.push_back(4 * i0 + 3);
381 indices.push_back(4 * i0 + 0);
382 indices.push_back(4 * i0 + 3);
383 indices.push_back(4 * i1 + 0);
384 indices.push_back(4 * i0 + 3);
385 indices.push_back(4 * i1 + 0);
386 indices.push_back(4 * i1 + 1);
389 float inset = (float)-cpfmax(0, 2 * mPointLineScale - radius);
390 float outset = (float)radius + mPointLineScale;
391 float r = outset - inset;
393 Vertex* vertices = PushVertices(4 * count, 3 * (5 * count - 2), &indices[0]);
394 for(int i = 0; i < count; i++)
396 cpVect v0 = verts[i];
397 cpVect v_prev = verts[(i + (count - 1)) % count];
398 cpVect v_next = verts[(i + (count + 1)) % count];
400 cpVect n1 = cpvnormalize(cpvrperp(cpvsub(v0, v_prev)));
401 cpVect n2 = cpvnormalize(cpvrperp(cpvsub(v_next, v0)));
402 cpVect of = cpvmult(cpvadd(n1, n2), 1.0 / (cpvdot(n1, n2) + 1.0f));
403 cpVect v = cpvadd(v0, cpvmult(of, inset));
405 vertices[4 * i + 0] = MakeVertex(v, 0.0f, 0.0f, 0.0f, fill, outline);
406 vertices[4 * i + 1] = MakeVertex(v, (float)n1.x, (float)n1.y, r, fill, outline);
407 vertices[4 * i + 2] = MakeVertex(v, (float)of.x, (float)of.y, r, fill, outline);
408 vertices[4 * i + 3] = MakeVertex(v, (float)n2.x, (float)n2.y, r, fill, outline);
412 void PhysicsDebugRenderer::DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color)
414 float r = (float)(size * 0.5f * mPointLineScale);
415 Vector4 fill(color.r, color.g, color.b, color.a);
416 static const uint16_t indices[] = {0, 1, 2, 0, 2, 3};
417 Vertex* vertex = PushVertices(4, 6, indices);
418 vertex[0] = MakeVertex(pos, -1, -1, r, fill, fill);
419 vertex[1] = MakeVertex(pos, -1, 1, r, fill, fill);
420 vertex[2] = MakeVertex(pos, 1, 1, r, fill, fill);
421 vertex[3] = MakeVertex(pos, 1, -1, r, fill, fill);
424 cpSpaceDebugColor PhysicsDebugRenderer::DrawColorForShape(cpShape* shape)
426 static cpSpaceDebugColor Colors[] = {
427 {0xb5 / 255.0f, 0x89 / 255.0f, 0x00 / 255.0f, 1.0f},
428 {0xcb / 255.0f, 0x4b / 255.0f, 0x16 / 255.0f, 1.0f},
429 {0xdc / 255.0f, 0x32 / 255.0f, 0x2f / 255.0f, 1.0f},
430 {0xd3 / 255.0f, 0x36 / 255.0f, 0x82 / 255.0f, 1.0f},
431 {0x6c / 255.0f, 0x71 / 255.0f, 0xc4 / 255.0f, 1.0f},
432 {0x26 / 255.0f, 0x8b / 255.0f, 0xd2 / 255.0f, 1.0f},
433 {0x2a / 255.0f, 0xa1 / 255.0f, 0x98 / 255.0f, 1.0f},
434 {0x85 / 255.0f, 0x99 / 255.0f, 0x00 / 255.0f, 1.0f},
437 if(cpShapeGetSensor(shape))
439 return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 0.1f};
443 cpBody* body = cpShapeGetBody(shape);
445 if(cpBodyIsSleeping(body))
447 return cpSpaceDebugColor{0x58 / 255.0f, 0x6e / 255.0f, 0x75 / 255.0f, 1.0f};
449 else if(cpBodyIsSleepThresholdExceeded(body, shape))
451 return cpSpaceDebugColor{0x93 / 255.0f, 0xa1 / 255.0f, 0xa1 / 255.0f, 1.0f};
455 uint32_t val = (uint32_t)cpShapeGetHashId(shape);
457 // scramble the bits up using Robert Jenkins' 32 bit integer hash function
458 val = (val + 0x7ed55d16) + (val << 12);
459 val = (val ^ 0xc761c23c) ^ (val >> 19);
460 val = (val + 0x165667b1) + (val << 5);
461 val = (val + 0xd3a2646c) ^ (val << 9);
462 val = (val + 0xfd7046c5) + (val << 3);
463 val = (val ^ 0xb55a4f09) ^ (val >> 16);
465 return Colors[val & 0x7];
469 return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 1.0f};
472 } // namespace Dali::Toolkit::Physics::Internal