[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-physics / internal / chipmunk-impl / chipmunk-physics-debug-renderer.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
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>
20
21 namespace
22 {
23 GLuint LoadShader(GLenum shaderType, const char* shaderSource)
24 {
25   GLuint shader = glCreateShader(shaderType);
26   if(shader != 0)
27   {
28     glShaderSource(shader, 1, &shaderSource, NULL);
29     glCompileShader(shader);
30     GLint compiled = 0;
31     glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
32     if(compiled != GL_TRUE)
33     {
34       GLint infoLen = 0;
35       glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
36
37       if(infoLen > 0)
38       {
39         std::vector<char> logBuffer;
40         logBuffer.resize(infoLen + 1);
41         glGetShaderInfoLog(shader, infoLen, NULL, &logBuffer[0]);
42         fprintf(stderr, "%s\n", &logBuffer[0]);
43         fflush(stderr);
44
45         glDeleteShader(shader);
46         shader = 0;
47       }
48     }
49   }
50   return shader;
51 }
52
53 GLuint CreateProgram(const char* vertexSource, const char* fragmentSource)
54 {
55   GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
56   if(!vertexShader)
57   {
58     return 0;
59   }
60   GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
61   if(!fragmentShader)
62   {
63     return 0;
64   }
65   GLuint program = glCreateProgram();
66   if(program)
67   {
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)
74     {
75       GLint bufLength = 0;
76       glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
77       if(bufLength)
78       {
79         std::vector<char> logBuffer;
80         logBuffer.resize(bufLength + 1);
81         glGetProgramInfoLog(program, bufLength, NULL, &logBuffer[0]);
82         fprintf(stderr, "%s\n", &logBuffer[0]);
83         fflush(stderr);
84       }
85       glDeleteProgram(program);
86       program = 0;
87     }
88   }
89   return program;
90 }
91 } // namespace
92
93 namespace Dali::Toolkit::Physics::Internal
94 {
95 static void DebugDrawCircleImpl(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data)
96 {
97   auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
98   debugRenderer->DrawCircle(pos, angle, radius, outlineColor, fillColor);
99 }
100
101 static void DebugDrawSegmentImpl(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data)
102 {
103   auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
104   debugRenderer->DrawSegment(a, b, color);
105 }
106
107 void DebugDrawFatSegmentImpl(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data)
108 {
109   auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
110   debugRenderer->DrawFatSegment(a, b, radius, outlineColor, fillColor);
111 }
112
113 void DebugDrawPolygonImpl(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data)
114 {
115   auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
116   debugRenderer->DrawPolygon(count, verts, radius, outlineColor, fillColor);
117 }
118
119 void DebugDrawDotImpl(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data)
120 {
121   auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
122   debugRenderer->DrawDot(size, pos, color);
123 }
124
125 cpSpaceDebugColor DebugDrawColorForShapeImpl(cpShape* shape, cpDataPointer data)
126 {
127   auto debugRenderer = static_cast<PhysicsDebugRenderer*>(data);
128   return debugRenderer->DrawColorForShape(shape);
129 }
130
131 std::unique_ptr<PhysicsDebugRenderer> PhysicsDebugRenderer::New(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor)
132 {
133   auto renderer = std::make_unique<PhysicsDebugRenderer>(width, height, camera, adaptor);
134
135   renderer->mRenderCallback = Dali::RenderCallback::New<PhysicsDebugRenderer>(renderer.get(), &PhysicsDebugRenderer::OnRender);
136   return renderer;
137 }
138
139 PhysicsDebugRenderer::PhysicsDebugRenderer(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor)
140 : mCamera(camera),
141   mWidth(width),
142   mHeight(height),
143   mAdaptor(*adaptor),
144   mPositionLocation(-1),
145   mUvsLocation(-1),
146   mRadiusLocation(-1),
147   mFillColourLocation(-1),
148   mOutlineColourLocation(-1),
149   mProjectionLocation(-1),
150   mModelViewLocation(-1),
151   mIndexBufferId(0u),
152   mVertexBufferId(0u),
153   mProgramId(0u)
154 {
155   mDebugDrawOptions.drawCircle     = DebugDrawCircleImpl;
156   mDebugDrawOptions.drawSegment    = DebugDrawSegmentImpl;
157   mDebugDrawOptions.drawFatSegment = DebugDrawFatSegmentImpl;
158   mDebugDrawOptions.drawPolygon    = DebugDrawPolygonImpl;
159   mDebugDrawOptions.drawDot        = DebugDrawDotImpl;
160
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;
169 }
170
171 bool PhysicsDebugRenderer::OnRender(const Dali::RenderCallbackInput& input)
172 {
173   if(mState == State::INIT)
174   {
175     Setup();
176     mState = State::RENDER;
177   }
178   glViewport(0, 0, mWidth, mHeight);
179
180   RenderLines(input);
181
182   return false;
183 }
184
185 // Run on first invocation of callback
186 void PhysicsDebugRenderer::Setup()
187 {
188   PrepareShader();
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");
194
195   mProjectionLocation = glGetUniformLocation(mProgramId, "projection");
196   mModelViewLocation  = glGetUniformLocation(mProgramId, "modelView");
197
198   glEnable(GL_DEPTH_TEST);
199   glViewport(0, 0, mWidth, mHeight);
200
201   glGenBuffers(1, &mIndexBufferId);
202   glGenBuffers(1, &mVertexBufferId);
203 }
204
205 void PhysicsDebugRenderer::UpdateWindowSize(Dali::Vector2 size)
206 {
207   mWidth  = size.width;
208   mHeight = size.height;
209 }
210
211 void PhysicsDebugRenderer::PrepareShader()
212 {
213   static const char glVertexShader[] =
214     "#version 300 es\n"
215     "in vec2 position;\n"
216     "in vec2 uvs;\n"
217     "in float radius;\n"
218     "in vec4 fillColor;\n"
219     "in vec4 outlineColor;\n"
220     "out vec2 v_uvs;\n"
221     "out vec4 v_fill;\n"
222     "out vec4 v_outline;\n"
223     "uniform mat4 projection;\n"
224     "uniform mat4 modelView;\n"
225     "void main()\n"
226     "{\n"
227     "    gl_Position = projection * modelView * vec4(position.xy+radius*uvs, 0.0, 1.0);\n"
228     "    v_uvs=uvs;\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"
233     "}\n";
234
235   static const char glFragmentShader[] =
236     "#version 300 es\n"
237     "precision mediump float;\n"
238     "in vec2 v_uvs;\n"
239     "in vec4 v_fill;\n"
240     "in vec4 v_outline;\n"
241     "out vec4 fragColor;\n"
242     "void main()\n"
243     "{\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"
251     "}\n";
252
253   mProgramId = CreateProgram(glVertexShader, glFragmentShader);
254 }
255
256 void PhysicsDebugRenderer::RenderLines(const Dali::RenderCallbackInput& input)
257 {
258   mModelViewMatrix.SetIdentity();
259   mProjectionMatrix = input.projection;
260
261   Matrix::Multiply(mModelViewMatrix, mModelViewMatrix, input.view);
262   glUseProgram(mProgramId);
263
264   // In theory, input.clippingBox should tell us the actor position in clip-space.
265   // But, it appears to be bugged.
266   glEnable(GL_BLEND);
267   glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
268
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);
273
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);
277
278   glVertexAttribPointer(mUvsLocation, 2, GL_FLOAT, GL_FALSE, stride, (const void*)8);
279   glEnableVertexAttribArray(mUvsLocation);
280
281   glVertexAttribPointer(mRadiusLocation, 1, GL_FLOAT, GL_FALSE, stride, (const void*)16);
282   glEnableVertexAttribArray(mRadiusLocation);
283
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);
288
289   glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix.AsFloat());
290   glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix.AsFloat());
291
292   glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_SHORT, 0);
293   mIndices.clear();
294   mVertices.clear();
295 }
296
297 PhysicsDebugRenderer::Vertex* PhysicsDebugRenderer::PushVertices(uint32_t vertexCount, uint32_t indexCount, const uint16_t* indices)
298 {
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)
303   {
304     mIndices.push_back(base + indices[i]);
305   }
306
307   return &mVertices[base];
308 }
309
310 PhysicsDebugRenderer::Vertex PhysicsDebugRenderer::MakeVertex(cpVect pos, float u, float v, float r, Vector4 fill, Vector4 outline)
311 {
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};
314 }
315
316 void PhysicsDebugRenderer::DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
317 {
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};
322
323   Vertex* vertices = PushVertices(4, 6, indices);
324
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);
329
330   DrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), 0.75f * radius)), outlineColor);
331 }
332
333 void PhysicsDebugRenderer::DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color)
334 {
335   DrawFatSegment(a, b, 0.0f, color, color);
336 }
337
338 void PhysicsDebugRenderer::DrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
339 {
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);
342
343   cpVect t = cpvnormalize(cpvsub(b, a));
344
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);
348
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);
357 }
358
359 void PhysicsDebugRenderer::DrawPolygon(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
360 {
361   Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
362   Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
363
364   std::vector<uint16_t> indices;
365   for(int i = 0; i < count - 2; i++)
366   {
367     indices.push_back(0);
368     indices.push_back(4 * (i + 1));
369     indices.push_back(4 * (i + 2));
370   }
371
372   // Polygon outline triangles.
373   for(int i0 = 0; i0 < count; i0++)
374   {
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);
388   }
389
390   float inset  = (float)-cpfmax(0, 2 * mPointLineScale - radius);
391   float outset = (float)radius + mPointLineScale;
392   float r      = outset - inset;
393
394   Vertex* vertices = PushVertices(4 * count, 3 * (5 * count - 2), &indices[0]);
395   for(int i = 0; i < count; i++)
396   {
397     cpVect v0     = verts[i];
398     cpVect v_prev = verts[(i + (count - 1)) % count];
399     cpVect v_next = verts[(i + (count + 1)) % count];
400
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));
405
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);
410   }
411 }
412
413 void PhysicsDebugRenderer::DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color)
414 {
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);
423 }
424
425 cpSpaceDebugColor PhysicsDebugRenderer::DrawColorForShape(cpShape* shape)
426 {
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},
436   };
437
438   if(cpShapeGetSensor(shape))
439   {
440     return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 0.1f};
441   }
442   else
443   {
444     cpBody* body = cpShapeGetBody(shape);
445
446     if(cpBodyIsSleeping(body))
447     {
448       return cpSpaceDebugColor{0x58 / 255.0f, 0x6e / 255.0f, 0x75 / 255.0f, 1.0f};
449     }
450     else if(cpBodyIsSleepThresholdExceeded(body, shape))
451     {
452       return cpSpaceDebugColor{0x93 / 255.0f, 0xa1 / 255.0f, 0xa1 / 255.0f, 1.0f};
453     }
454     else
455     {
456       uint32_t val = (uint32_t)cpShapeGetHashId(shape);
457
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);
465
466       return Colors[val & 0x7];
467     }
468   }
469 }
470
471 } // namespace Dali::Toolkit::Physics::Internal