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.constraintColor = cpSpaceDebugColor{0.5f, 0.5f, 0.5f, 0.9f};
167 mDebugDrawOptions.collisionPointColor = cpSpaceDebugColor{1.0f, 0.0f, 0.0f, 1.0f};
168 mDebugDrawOptions.data = this;
171 bool PhysicsDebugRenderer::OnRender(const Dali::RenderCallbackInput& input)
173 if(mState == State::INIT)
176 mState = State::RENDER;
178 glViewport(0, 0, mWidth, mHeight);
185 // Run on first invocation of callback
186 void PhysicsDebugRenderer::Setup()
189 mPositionLocation = glGetAttribLocation(mProgramId, "position");
190 mUvsLocation = glGetAttribLocation(mProgramId, "uvs");
191 mRadiusLocation = glGetAttribLocation(mProgramId, "radius");
192 mFillColourLocation = glGetAttribLocation(mProgramId, "fillColor");
193 mOutlineColourLocation = glGetAttribLocation(mProgramId, "outlineColor");
195 mProjectionLocation = glGetUniformLocation(mProgramId, "projection");
196 mModelViewLocation = glGetUniformLocation(mProgramId, "modelView");
198 glEnable(GL_DEPTH_TEST);
199 glViewport(0, 0, mWidth, mHeight);
201 glGenBuffers(1, &mIndexBufferId);
202 glGenBuffers(1, &mVertexBufferId);
205 void PhysicsDebugRenderer::UpdateWindowSize(Dali::Vector2 size)
208 mHeight = size.height;
211 void PhysicsDebugRenderer::PrepareShader()
213 static const char glVertexShader[] =
215 "in vec2 position;\n"
218 "in vec4 fillColor;\n"
219 "in vec4 outlineColor;\n"
222 "out vec4 v_outline;\n"
223 "uniform mat4 projection;\n"
224 "uniform mat4 modelView;\n"
227 " gl_Position = projection * modelView * vec4(position.xy+radius*uvs, 0.0, 1.0);\n"
229 " v_fill = fillColor;\n"
230 " v_fill.rgb *= v_fill.a;\n"
231 " v_outline = outlineColor;\n"
232 " v_outline.a *= v_outline.a;\n"
235 static const char glFragmentShader[] =
237 "precision mediump float;\n"
240 "in vec4 v_outline;\n"
241 "out vec4 fragColor;\n"
244 " float len=length(v_uvs);\n"
245 " float fw = length(vec2(dFdx(len), dFdy(len)));\n"
246 " float mask=smoothstep(-1.0, fw-1.0, -len);\n"
247 " float outline=1.0-fw;\n"
248 " float outline_mask=smoothstep(outline-fw, outline, len);\n"
249 " vec4 color = v_fill + (v_outline - v_fill*v_outline.a)*outline_mask;\n"
250 " fragColor = color*mask;\n"
253 mProgramId = CreateProgram(glVertexShader, glFragmentShader);
256 void PhysicsDebugRenderer::RenderLines(const Dali::RenderCallbackInput& input)
258 mModelViewMatrix.SetIdentity();
259 mProjectionMatrix = input.projection;
261 Matrix::Multiply(mModelViewMatrix, mModelViewMatrix, input.view);
262 glUseProgram(mProgramId);
264 // In theory, input.clippingBox should tell us the actor position in clip-space.
265 // But, it appears to be bugged.
267 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
269 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferId);
270 glBufferData(GL_ELEMENT_ARRAY_BUFFER, GLsizeiptr(mIndices.size() * sizeof(uint16_t)), &mIndices[0], GL_STATIC_DRAW);
271 glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferId);
272 glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(mVertices.size() * sizeof(Vertex)), &mVertices[0], GL_STATIC_DRAW);
274 GLint stride = 52; // 4*(2 + 2 + 1 + 4 + 4) = 4*13=52
275 glVertexAttribPointer(mPositionLocation, 2, GL_FLOAT, GL_FALSE, stride, 0);
276 glEnableVertexAttribArray(mPositionLocation);
278 glVertexAttribPointer(mUvsLocation, 2, GL_FLOAT, GL_FALSE, stride, (const void*)8);
279 glEnableVertexAttribArray(mUvsLocation);
281 glVertexAttribPointer(mRadiusLocation, 1, GL_FLOAT, GL_FALSE, stride, (const void*)16);
282 glEnableVertexAttribArray(mRadiusLocation);
284 glVertexAttribPointer(mFillColourLocation, 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<const void*>(20));
285 glEnableVertexAttribArray(mFillColourLocation);
286 glVertexAttribPointer(mOutlineColourLocation, 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<const void*>(36));
287 glEnableVertexAttribArray(mOutlineColourLocation);
289 glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix.AsFloat());
290 glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix.AsFloat());
292 glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_SHORT, 0);
297 PhysicsDebugRenderer::Vertex* PhysicsDebugRenderer::PushVertices(uint32_t vertexCount, uint32_t indexCount, const uint16_t* indices)
299 auto base = mVertices.size();
300 mVertices.resize(mVertices.size() + vertexCount);
301 mIndices.reserve(mIndices.size() + indexCount);
302 for(uint32_t i = 0; i < indexCount; ++i)
304 mIndices.push_back(base + indices[i]);
307 return &mVertices[base];
310 PhysicsDebugRenderer::Vertex PhysicsDebugRenderer::MakeVertex(cpVect pos, float u, float v, float r, Vector4 fill, Vector4 outline)
312 auto daliPos = mAdaptor.TranslateFromPhysicsSpace(Vector3((float)pos.x, (float)pos.y, 0.0f));
313 return Vertex{Vector2(daliPos.x, daliPos.y), Vector2(u, v), r, fill, outline};
316 void PhysicsDebugRenderer::DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
318 float r = (float)radius + mPointLineScale;
319 Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
320 Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
321 static const uint16_t indices[] = {0, 1, 2, 0, 2, 3};
323 Vertex* vertices = PushVertices(4, 6, indices);
325 vertices[0] = MakeVertex(pos, -1, -1, r, fill, outline);
326 vertices[1] = MakeVertex(pos, -1, 1, r, fill, outline);
327 vertices[2] = MakeVertex(pos, 1, 1, r, fill, outline);
328 vertices[3] = MakeVertex(pos, 1, -1, r, fill, outline);
330 DrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), 0.75f * radius)), outlineColor);
333 void PhysicsDebugRenderer::DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color)
335 DrawFatSegment(a, b, 0.0f, color, color);
338 void PhysicsDebugRenderer::DrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
340 static const uint16_t indices[] = {0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7};
341 Vertex* vertices = PushVertices(8, 18, indices);
343 cpVect t = cpvnormalize(cpvsub(b, a));
345 float r = (float)radius * mPointLineScale;
346 Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
347 Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
349 vertices[0] = MakeVertex(a, (-t.x + t.y), (-t.x - t.y), r, fill, outline);
350 vertices[1] = MakeVertex(a, (-t.x - t.y), (+t.x - t.y), r, fill, outline);
351 vertices[2] = MakeVertex(a, (-0.0 + t.y), (-t.x + 0.0), r, fill, outline);
352 vertices[3] = MakeVertex(a, (-0.0 - t.y), (+t.x + 0.0), r, fill, outline);
353 vertices[4] = MakeVertex(a, (+0.0 + t.y), (-t.x - 0.0), r, fill, outline);
354 vertices[5] = MakeVertex(a, (+0.0 - t.y), (+t.x - 0.0), r, fill, outline);
355 vertices[6] = MakeVertex(a, (+t.x + t.y), (-t.x + t.y), r, fill, outline);
356 vertices[7] = MakeVertex(a, (+t.x - t.y), (+t.x + t.y), r, fill, outline);
359 void PhysicsDebugRenderer::DrawPolygon(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
361 Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
362 Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
364 std::vector<uint16_t> indices;
365 for(int i = 0; i < count - 2; i++)
367 indices.push_back(0);
368 indices.push_back(4 * (i + 1));
369 indices.push_back(4 * (i + 2));
372 // Polygon outline triangles.
373 for(int i0 = 0; i0 < count; i0++)
375 int i1 = (i0 + 1) % count;
376 indices.push_back(4 * i0 + 0);
377 indices.push_back(4 * i0 + 1);
378 indices.push_back(4 * i0 + 2);
379 indices.push_back(4 * i0 + 0);
380 indices.push_back(4 * i0 + 2);
381 indices.push_back(4 * i0 + 3);
382 indices.push_back(4 * i0 + 0);
383 indices.push_back(4 * i0 + 3);
384 indices.push_back(4 * i1 + 0);
385 indices.push_back(4 * i0 + 3);
386 indices.push_back(4 * i1 + 0);
387 indices.push_back(4 * i1 + 1);
390 float inset = (float)-cpfmax(0, 2 * mPointLineScale - radius);
391 float outset = (float)radius + mPointLineScale;
392 float r = outset - inset;
394 Vertex* vertices = PushVertices(4 * count, 3 * (5 * count - 2), &indices[0]);
395 for(int i = 0; i < count; i++)
397 cpVect v0 = verts[i];
398 cpVect v_prev = verts[(i + (count - 1)) % count];
399 cpVect v_next = verts[(i + (count + 1)) % count];
401 cpVect n1 = cpvnormalize(cpvrperp(cpvsub(v0, v_prev)));
402 cpVect n2 = cpvnormalize(cpvrperp(cpvsub(v_next, v0)));
403 cpVect of = cpvmult(cpvadd(n1, n2), 1.0 / (cpvdot(n1, n2) + 1.0f));
404 cpVect v = cpvadd(v0, cpvmult(of, inset));
406 vertices[4 * i + 0] = MakeVertex(v, 0.0f, 0.0f, 0.0f, fill, outline);
407 vertices[4 * i + 1] = MakeVertex(v, (float)n1.x, (float)n1.y, r, fill, outline);
408 vertices[4 * i + 2] = MakeVertex(v, (float)of.x, (float)of.y, r, fill, outline);
409 vertices[4 * i + 3] = MakeVertex(v, (float)n2.x, (float)n2.y, r, fill, outline);
413 void PhysicsDebugRenderer::DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color)
415 float r = (float)(size * 0.5f * mPointLineScale);
416 Vector4 fill(color.r, color.g, color.b, color.a);
417 static const uint16_t indices[] = {0, 1, 2, 0, 2, 3};
418 Vertex* vertex = PushVertices(4, 6, indices);
419 vertex[0] = MakeVertex(pos, -1, -1, r, fill, fill);
420 vertex[1] = MakeVertex(pos, -1, 1, r, fill, fill);
421 vertex[2] = MakeVertex(pos, 1, 1, r, fill, fill);
422 vertex[3] = MakeVertex(pos, 1, -1, r, fill, fill);
425 cpSpaceDebugColor PhysicsDebugRenderer::DrawColorForShape(cpShape* shape)
427 static cpSpaceDebugColor Colors[] = {
428 {0xb5 / 255.0f, 0x89 / 255.0f, 0x00 / 255.0f, 1.0f},
429 {0xcb / 255.0f, 0x4b / 255.0f, 0x16 / 255.0f, 1.0f},
430 {0xdc / 255.0f, 0x32 / 255.0f, 0x2f / 255.0f, 1.0f},
431 {0xd3 / 255.0f, 0x36 / 255.0f, 0x82 / 255.0f, 1.0f},
432 {0x6c / 255.0f, 0x71 / 255.0f, 0xc4 / 255.0f, 1.0f},
433 {0x26 / 255.0f, 0x8b / 255.0f, 0xd2 / 255.0f, 1.0f},
434 {0x2a / 255.0f, 0xa1 / 255.0f, 0x98 / 255.0f, 1.0f},
435 {0x85 / 255.0f, 0x99 / 255.0f, 0x00 / 255.0f, 1.0f},
438 if(cpShapeGetSensor(shape))
440 return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 0.1f};
444 cpBody* body = cpShapeGetBody(shape);
446 if(cpBodyIsSleeping(body))
448 return cpSpaceDebugColor{0x58 / 255.0f, 0x6e / 255.0f, 0x75 / 255.0f, 1.0f};
450 else if(cpBodyIsSleepThresholdExceeded(body, shape))
452 return cpSpaceDebugColor{0x93 / 255.0f, 0xa1 / 255.0f, 0xa1 / 255.0f, 1.0f};
456 uint32_t val = (uint32_t)cpShapeGetHashId(shape);
458 // scramble the bits up using Robert Jenkins' 32 bit integer hash function
459 val = (val + 0x7ed55d16) + (val << 12);
460 val = (val ^ 0xc761c23c) ^ (val >> 19);
461 val = (val + 0x165667b1) + (val << 5);
462 val = (val + 0xd3a2646c) ^ (val << 9);
463 val = (val + 0xfd7046c5) + (val << 3);
464 val = (val ^ 0xb55a4f09) ^ (val >> 16);
466 return Colors[val & 0x7];
471 } // namespace Dali::Toolkit::Physics::Internal