Merge "Apply fittingMode lazy when resource is not ready" into 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.collisionPointColor = cpSpaceDebugColor{1.0f, 0.0f, 0.0f, 1.0f};
167   mDebugDrawOptions.data                = this;
168 }
169
170 bool PhysicsDebugRenderer::OnRender(const Dali::RenderCallbackInput& input)
171 {
172   if(mState == State::INIT)
173   {
174     Setup();
175     mState = State::RENDER;
176   }
177   glViewport(0, 0, mWidth, mHeight);
178
179   RenderLines(input);
180
181   return false;
182 }
183
184 // Run on first invocation of callback
185 void PhysicsDebugRenderer::Setup()
186 {
187   PrepareShader();
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");
193
194   mProjectionLocation = glGetUniformLocation(mProgramId, "projection");
195   mModelViewLocation  = glGetUniformLocation(mProgramId, "modelView");
196
197   glEnable(GL_DEPTH_TEST);
198   glViewport(0, 0, mWidth, mHeight);
199
200   glGenBuffers(1, &mIndexBufferId);
201   glGenBuffers(1, &mVertexBufferId);
202 }
203
204 void PhysicsDebugRenderer::UpdateWindowSize(Dali::Vector2 size)
205 {
206   mWidth  = size.width;
207   mHeight = size.height;
208 }
209
210 void PhysicsDebugRenderer::PrepareShader()
211 {
212   static const char glVertexShader[] =
213     "#version 300 es\n"
214     "in vec2 position;\n"
215     "in vec2 uvs;\n"
216     "in float radius;\n"
217     "in vec4 fillColor;\n"
218     "in vec4 outlineColor;\n"
219     "out vec2 v_uvs;\n"
220     "out vec4 v_fill;\n"
221     "out vec4 v_outline;\n"
222     "uniform mat4 projection;\n"
223     "uniform mat4 modelView;\n"
224     "void main()\n"
225     "{\n"
226     "    gl_Position = projection * modelView * vec4(position.xy+radius*uvs, 0.0, 1.0);\n"
227     "    v_uvs=uvs;\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"
232     "}\n";
233
234   static const char glFragmentShader[] =
235     "#version 300 es\n"
236     "precision mediump float;\n"
237     "in vec2 v_uvs;\n"
238     "in vec4 v_fill;\n"
239     "in vec4 v_outline;\n"
240     "out vec4 fragColor;\n"
241     "void main()\n"
242     "{\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"
250     "}\n";
251
252   mProgramId = CreateProgram(glVertexShader, glFragmentShader);
253 }
254
255 void PhysicsDebugRenderer::RenderLines(const Dali::RenderCallbackInput& input)
256 {
257   mModelViewMatrix.SetIdentity();
258   mProjectionMatrix = input.projection;
259
260   Matrix::Multiply(mModelViewMatrix, mModelViewMatrix, input.view);
261   glUseProgram(mProgramId);
262
263   // In theory, input.clippingBox should tell us the actor position in clip-space.
264   // But, it appears to be bugged.
265   glEnable(GL_BLEND);
266   glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
267
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);
272
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);
276
277   glVertexAttribPointer(mUvsLocation, 2, GL_FLOAT, GL_FALSE, stride, (const void*)8);
278   glEnableVertexAttribArray(mUvsLocation);
279
280   glVertexAttribPointer(mRadiusLocation, 1, GL_FLOAT, GL_FALSE, stride, (const void*)16);
281   glEnableVertexAttribArray(mRadiusLocation);
282
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);
287
288   glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix.AsFloat());
289   glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix.AsFloat());
290
291   glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_SHORT, 0);
292   mIndices.clear();
293   mVertices.clear();
294 }
295
296 PhysicsDebugRenderer::Vertex* PhysicsDebugRenderer::PushVertices(uint32_t vertexCount, uint32_t indexCount, const uint16_t* indices)
297 {
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)
302   {
303     mIndices.push_back(base + indices[i]);
304   }
305
306   return &mVertices[base];
307 }
308
309 PhysicsDebugRenderer::Vertex PhysicsDebugRenderer::MakeVertex(cpVect pos, float u, float v, float r, Vector4 fill, Vector4 outline)
310 {
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};
313 }
314
315 void PhysicsDebugRenderer::DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
316 {
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};
321
322   Vertex* vertices = PushVertices(4, 6, indices);
323
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);
328
329   DrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), 0.75f * radius)), outlineColor);
330 }
331
332 void PhysicsDebugRenderer::DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color)
333 {
334   DrawFatSegment(a, b, 0.0f, color, color);
335 }
336
337 void PhysicsDebugRenderer::DrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
338 {
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);
341
342   cpVect t = cpvnormalize(cpvsub(b, a));
343
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);
347
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);
356 }
357
358 void PhysicsDebugRenderer::DrawPolygon(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor)
359 {
360   Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a);
361   Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
362
363   std::vector<uint16_t> indices;
364   for(int i = 0; i < count - 2; i++)
365   {
366     indices.push_back(0);
367     indices.push_back(4 * (i + 1));
368     indices.push_back(4 * (i + 2));
369   }
370
371   // Polygon outline triangles.
372   for(int i0 = 0; i0 < count; i0++)
373   {
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);
387   }
388
389   float inset  = (float)-cpfmax(0, 2 * mPointLineScale - radius);
390   float outset = (float)radius + mPointLineScale;
391   float r      = outset - inset;
392
393   Vertex* vertices = PushVertices(4 * count, 3 * (5 * count - 2), &indices[0]);
394   for(int i = 0; i < count; i++)
395   {
396     cpVect v0     = verts[i];
397     cpVect v_prev = verts[(i + (count - 1)) % count];
398     cpVect v_next = verts[(i + (count + 1)) % count];
399
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));
404
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);
409   }
410 }
411
412 void PhysicsDebugRenderer::DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color)
413 {
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);
422 }
423
424 cpSpaceDebugColor PhysicsDebugRenderer::DrawColorForShape(cpShape* shape)
425 {
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},
435   };
436
437   if(cpShapeGetSensor(shape))
438   {
439     return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 0.1f};
440   }
441   else
442   {
443     cpBody* body = cpShapeGetBody(shape);
444
445     if(cpBodyIsSleeping(body))
446     {
447       return cpSpaceDebugColor{0x58 / 255.0f, 0x6e / 255.0f, 0x75 / 255.0f, 1.0f};
448     }
449     else if(cpBodyIsSleepThresholdExceeded(body, shape))
450     {
451       return cpSpaceDebugColor{0x93 / 255.0f, 0xa1 / 255.0f, 0xa1 / 255.0f, 1.0f};
452     }
453     else
454     {
455       uint32_t val = (uint32_t)cpShapeGetHashId(shape);
456
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);
464
465       return Colors[val & 0x7];
466     }
467   }
468
469   return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 1.0f};
470 }
471
472 } // namespace Dali::Toolkit::Physics::Internal