From: David Steele Date: Wed, 6 Sep 2023 15:39:35 +0000 (+0100) Subject: Added debug renderer for Chipmunk X-Git-Tag: dali_2.2.44~9^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F15%2F298415%2F3;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git Added debug renderer for Chipmunk Change-Id: I0729e53256aeb0af395bf76aeab32305708d5a95 --- diff --git a/automated-tests/src/dali-physics2d/utc-Dali-PhysicsAdaptor.cpp b/automated-tests/src/dali-physics2d/utc-Dali-PhysicsAdaptor.cpp index b2d033a..cf846bb 100644 --- a/automated-tests/src/dali-physics2d/utc-Dali-PhysicsAdaptor.cpp +++ b/automated-tests/src/dali-physics2d/utc-Dali-PhysicsAdaptor.cpp @@ -60,6 +60,49 @@ cpBody* CreateBody(cpSpace* space) return body; } +// Defines a PolyShape +cpBody* CreateHexBody(cpSpace* space) +{ + const float MASS = 10.0f; + const float RADIUS = 26.0f; + const float ELASTICITY = 0.5f; + const float FRICTION = 0.5f; + + cpVect hexagon[6]; + for(int i = 0; i < 6; i++) + { + cpFloat angle = -CP_PI * 2.0f * i / 6.0f; + hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), RADIUS - 1.0f); + } + + cpBody* body = cpSpaceAddBody(space, cpBodyNew(MASS, cpMomentForPoly(MASS, 6, hexagon, cpvzero, 0.0f))); + cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, 1.0f)); + + cpShapeSetElasticity(shape, ELASTICITY); + cpShapeSetFriction(shape, FRICTION); + + return body; +} + +// Defines a SegmentShape +cpBody* CreateSegBody(cpSpace* space) +{ + const float MASS = 10.0f; + const float RADIUS = 26.0f; + const float ELASTICITY = 0.5f; + const float FRICTION = 0.5f; + + cpVect a = cpv(0, 100); + cpVect b = cpv(100, 0); + cpBody* body = cpSpaceAddBody(space, cpBodyNew(MASS, cpMomentForSegment(MASS, a, b, 0.0f))); + cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, RADIUS)); + + cpShapeSetElasticity(shape, ELASTICITY); + cpShapeSetFriction(shape, FRICTION); + + return body; +} + int UtcDaliPhysics2DCreateAdaptorP1(void) { ToolkitTestApplication application; @@ -290,7 +333,46 @@ int UtcDaliPhysics2DAdaptorCreateDebugLayer(void) Window window = DevelWindow::Get(rootActor); Layer layer = adaptor.CreateDebugLayer(window); - DALI_TEST_CHECK(!layer); + DALI_TEST_CHECK(layer); + + adaptor.SetDebugState(PhysicsAdaptor::DebugState::ON); + + cpBody* body{nullptr}; + cpBody* body2{nullptr}; + cpBody* body3{nullptr}; + { + auto accessor = adaptor.GetPhysicsAccessor(); + auto space = accessor->GetNative().Get(); + + body = CreateBody(space); + Dali::Actor ballActor = Toolkit::ImageView::New("gallery-small-1.jpg"); + auto physicsActor = adaptor.AddActorBody(ballActor, body); + cpBodySetPosition(body, cpv(0, 0)); + + // Constraint should create a dot in debug + cpBody* staticBody = cpSpaceGetStaticBody(space); + cpSpaceAddConstraint(space, cpPivotJointNew(staticBody, body, cpv(10, 10))); + + body2 = CreateHexBody(space); + Dali::Actor ballActor2 = Toolkit::ImageView::New("gallery-small-1.jpg"); + auto physicsActor2 = adaptor.AddActorBody(ballActor2, body2); + cpBodySleep(body2); + + body3 = CreateSegBody(space); + Dali::Actor ballActor3 = Toolkit::ImageView::New("gallery-small-1.jpg"); + auto physicsActor3 = adaptor.AddActorBody(ballActor3, body3); + } + Test::WaitForEventThreadTrigger(1); + + // Render - if it doesn't crash, great! + application.SendNotification(); + application.Render(); + + Uint16Pair size2(480, 640); + adaptor.SetTransformAndSize(transform, size2); + + application.SendNotification(); + application.Render(); END_TEST; } diff --git a/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.cpp b/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.cpp index 1f52062..923927a 100644 --- a/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.cpp +++ b/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.cpp @@ -71,7 +71,42 @@ void ChipmunkPhysicsAdaptor::OnInitialize(const Dali::Matrix& transform, Uint16P Layer ChipmunkPhysicsAdaptor::CreateDebugLayer(Dali::Window window) { - return Layer(); + Layer debugLayer; + + auto renderTaskList = window.GetRenderTaskList(); + auto renderTask = renderTaskList.GetTask(0); + auto windowSize = window.GetSize(); + + debugLayer = Layer::New(); + debugLayer[Actor::Property::NAME] = "PhysicsDebugLayer"; + debugLayer[Actor::Property::ANCHOR_POINT] = Dali::AnchorPoint::CENTER; + debugLayer[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER; + + Constraint positionConstraint = Constraint::New(debugLayer, Actor::Property::POSITION, EqualToConstraint()); + positionConstraint.AddSource(Source(mRootActor, Actor::Property::POSITION)); + positionConstraint.Apply(); + Constraint sizeConstraint = Constraint::New(debugLayer, Actor::Property::SIZE, EqualToConstraint()); + sizeConstraint.AddSource(Source(mRootActor, Actor::Property::SIZE)); + sizeConstraint.Apply(); + + auto world = static_cast(mPhysicsWorld.get()); + + std::unique_ptr debugRenderer = PhysicsDebugRenderer::New(windowSize.GetWidth(), windowSize.GetHeight(), renderTask.GetCameraActor(), this); + + mDebugActor = DrawableActor::New(*(debugRenderer->GetCallback().get())); + world->SetDebugRenderer(debugRenderer.release()); + + mDebugActor[Actor::Property::ANCHOR_POINT] = Dali::AnchorPoint::CENTER; + mDebugActor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER; + + Constraint sizeConstraint2 = Constraint::New(mDebugActor, Actor::Property::SIZE, EqualToConstraint()); + sizeConstraint2.AddSource(ParentSource(Actor::Property::SIZE)); + sizeConstraint2.Apply(); + + debugLayer.Add(mDebugActor); + + window.Add(debugLayer); + return debugLayer; } void ChipmunkPhysicsAdaptor::SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair worldSize) @@ -82,6 +117,14 @@ void ChipmunkPhysicsAdaptor::SetTransformAndSize(const Dali::Matrix& transform, mSize = worldSize; GetRootActor()[Actor::Property::SIZE] = Vector3(worldSize.GetWidth(), worldSize.GetHeight(), 0); + + auto world = static_cast(mPhysicsWorld.get()); + if(world->HasDebugRenderer()) + { + Actor layer = mDebugActor.GetParent(); + layer[Actor::Property::SIZE] = Vector3(worldSize); + world->GetDebugRenderer().UpdateWindowSize(worldSize); + } } PhysicsActorPtr ChipmunkPhysicsAdaptor::AddActorBody(Dali::Actor actor, Dali::Any body) diff --git a/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.h b/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.h index 48715e3..55f515d 100644 --- a/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.h +++ b/dali-physics/internal/chipmunk-impl/chipmunk-physics-adaptor-impl.h @@ -106,6 +106,10 @@ public: * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ProjectPoint */ Dali::Vector3 ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance) override; + +private: + Actor mDebugActor; + // Physics world owns debug renderer }; } // namespace Dali::Toolkit::Physics::Internal diff --git a/dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.cpp b/dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.cpp new file mode 100644 index 0000000..2e60d97 --- /dev/null +++ b/dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.cpp @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace +{ +GLuint LoadShader(GLenum shaderType, const char* shaderSource) +{ + GLuint shader = glCreateShader(shaderType); + if(shader != 0) + { + glShaderSource(shader, 1, &shaderSource, NULL); + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if(compiled != GL_TRUE) + { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + + if(infoLen > 0) + { + std::vector logBuffer; + logBuffer.resize(infoLen + 1); + glGetShaderInfoLog(shader, infoLen, NULL, &logBuffer[0]); + fprintf(stderr, "%s\n", &logBuffer[0]); + fflush(stderr); + + glDeleteShader(shader); + shader = 0; + } + } + } + return shader; +} + +GLuint CreateProgram(const char* vertexSource, const char* fragmentSource) +{ + GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource); + if(!vertexShader) + { + return 0; + } + GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource); + if(!fragmentShader) + { + return 0; + } + GLuint program = glCreateProgram(); + if(program) + { + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if(linkStatus != GL_TRUE) + { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if(bufLength) + { + std::vector logBuffer; + logBuffer.resize(bufLength + 1); + glGetProgramInfoLog(program, bufLength, NULL, &logBuffer[0]); + fprintf(stderr, "%s\n", &logBuffer[0]); + fflush(stderr); + } + glDeleteProgram(program); + program = 0; + } + } + return program; +} +} // namespace + +namespace Dali::Toolkit::Physics::Internal +{ +static void DebugDrawCircleImpl(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data) +{ + auto debugRenderer = static_cast(data); + debugRenderer->DrawCircle(pos, angle, radius, outlineColor, fillColor); +} + +static void DebugDrawSegmentImpl(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data) +{ + auto debugRenderer = static_cast(data); + debugRenderer->DrawSegment(a, b, color); +} + +void DebugDrawFatSegmentImpl(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data) +{ + auto debugRenderer = static_cast(data); + debugRenderer->DrawFatSegment(a, b, radius, outlineColor, fillColor); +} + +void DebugDrawPolygonImpl(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data) +{ + auto debugRenderer = static_cast(data); + debugRenderer->DrawPolygon(count, verts, radius, outlineColor, fillColor); +} + +void DebugDrawDotImpl(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data) +{ + auto debugRenderer = static_cast(data); + debugRenderer->DrawDot(size, pos, color); +} + +cpSpaceDebugColor DebugDrawColorForShapeImpl(cpShape* shape, cpDataPointer data) +{ + auto debugRenderer = static_cast(data); + return debugRenderer->DrawColorForShape(shape); +} + +std::unique_ptr PhysicsDebugRenderer::New(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor) +{ + auto renderer = std::make_unique(width, height, camera, adaptor); + + renderer->mRenderCallback = Dali::RenderCallback::New(renderer.get(), &PhysicsDebugRenderer::OnRender); + return renderer; +} + +PhysicsDebugRenderer::PhysicsDebugRenderer(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor) +: mCamera(camera), + mWidth(width), + mHeight(height), + mAdaptor(*adaptor), + mPositionLocation(-1), + mUvsLocation(-1), + mRadiusLocation(-1), + mFillColourLocation(-1), + mOutlineColourLocation(-1), + mProjectionLocation(-1), + mModelViewLocation(-1), + mIndexBufferId(0u), + mVertexBufferId(0u), + mProgramId(0u) +{ + mDebugDrawOptions.drawCircle = DebugDrawCircleImpl; + mDebugDrawOptions.drawSegment = DebugDrawSegmentImpl; + mDebugDrawOptions.drawFatSegment = DebugDrawFatSegmentImpl; + mDebugDrawOptions.drawPolygon = DebugDrawPolygonImpl; + mDebugDrawOptions.drawDot = DebugDrawDotImpl; + + mDebugDrawOptions.flags = static_cast(CP_SPACE_DEBUG_DRAW_SHAPES | + CP_SPACE_DEBUG_DRAW_COLLISION_POINTS | + CP_SPACE_DEBUG_DRAW_CONSTRAINTS); + mDebugDrawOptions.colorForShape = DebugDrawColorForShapeImpl; + mDebugDrawOptions.shapeOutlineColor = cpSpaceDebugColor{0.0f, 1.0f, 1.0f, 0.9f}; + mDebugDrawOptions.collisionPointColor = cpSpaceDebugColor{1.0f, 0.0f, 0.0f, 1.0f}; + mDebugDrawOptions.data = this; +} + +bool PhysicsDebugRenderer::OnRender(const Dali::RenderCallbackInput& input) +{ + if(mState == State::INIT) + { + Setup(); + mState = State::RENDER; + } + glViewport(0, 0, mWidth, mHeight); + + RenderLines(input); + + return false; +} + +// Run on first invocation of callback +void PhysicsDebugRenderer::Setup() +{ + PrepareShader(); + mPositionLocation = glGetAttribLocation(mProgramId, "position"); + mUvsLocation = glGetAttribLocation(mProgramId, "uvs"); + mRadiusLocation = glGetAttribLocation(mProgramId, "radius"); + mFillColourLocation = glGetAttribLocation(mProgramId, "fillColor"); + mOutlineColourLocation = glGetAttribLocation(mProgramId, "outlineColor"); + + mProjectionLocation = glGetUniformLocation(mProgramId, "projection"); + mModelViewLocation = glGetUniformLocation(mProgramId, "modelView"); + + glEnable(GL_DEPTH_TEST); + glViewport(0, 0, mWidth, mHeight); + + glGenBuffers(1, &mIndexBufferId); + glGenBuffers(1, &mVertexBufferId); +} + +void PhysicsDebugRenderer::UpdateWindowSize(Dali::Vector2 size) +{ + mWidth = size.width; + mHeight = size.height; +} + +void PhysicsDebugRenderer::PrepareShader() +{ + static const char glVertexShader[] = + "#version 300 es\n" + "in vec2 position;\n" + "in vec2 uvs;\n" + "in float radius;\n" + "in vec4 fillColor;\n" + "in vec4 outlineColor;\n" + "out vec2 v_uvs;\n" + "out vec4 v_fill;\n" + "out vec4 v_outline;\n" + "uniform mat4 projection;\n" + "uniform mat4 modelView;\n" + "void main()\n" + "{\n" + " gl_Position = projection * modelView * vec4(position.xy+radius*uvs, 0.0, 1.0);\n" + " v_uvs=uvs;\n" + " v_fill = fillColor;\n" + " v_fill.rgb *= v_fill.a;\n" + " v_outline = outlineColor;\n" + " v_outline.a *= v_outline.a;\n" + "}\n"; + + static const char glFragmentShader[] = + "#version 300 es\n" + "precision mediump float;\n" + "in vec2 v_uvs;\n" + "in vec4 v_fill;\n" + "in vec4 v_outline;\n" + "out vec4 fragColor;\n" + "void main()\n" + "{\n" + " float len=length(v_uvs);\n" + " float fw = length(vec2(dFdx(len), dFdy(len)));\n" + " float mask=smoothstep(-1.0, fw-1.0, -len);\n" + " float outline=1.0-fw;\n" + " float outline_mask=smoothstep(outline-fw, outline, len);\n" + " vec4 color = v_fill + (v_outline - v_fill*v_outline.a)*outline_mask;\n" + " fragColor = color*mask;\n" + "}\n"; + + mProgramId = CreateProgram(glVertexShader, glFragmentShader); +} + +void PhysicsDebugRenderer::RenderLines(const Dali::RenderCallbackInput& input) +{ + mModelViewMatrix.SetIdentity(); + mProjectionMatrix = input.projection; + + Matrix::Multiply(mModelViewMatrix, mModelViewMatrix, input.view); + glUseProgram(mProgramId); + + // In theory, input.clippingBox should tell us the actor position in clip-space. + // But, it appears to be bugged. + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferId); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndices.size() * sizeof(uint16_t), &mIndices[0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferId); + glBufferData(GL_ARRAY_BUFFER, mVertices.size() * sizeof(Vertex), &mVertices[0], GL_STATIC_DRAW); + + GLint stride = 52; // 4*(2 + 2 + 1 + 4 + 4) = 4*13=52 + glVertexAttribPointer(mPositionLocation, 2, GL_FLOAT, GL_FALSE, stride, 0); + glEnableVertexAttribArray(mPositionLocation); + + glVertexAttribPointer(mUvsLocation, 2, GL_FLOAT, GL_FALSE, stride, (const void*)8); + glEnableVertexAttribArray(mUvsLocation); + + glVertexAttribPointer(mRadiusLocation, 1, GL_FLOAT, GL_FALSE, stride, (const void*)16); + glEnableVertexAttribArray(mRadiusLocation); + + glVertexAttribPointer(mFillColourLocation, 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(20)); + glEnableVertexAttribArray(mFillColourLocation); + glVertexAttribPointer(mOutlineColourLocation, 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(36)); + glEnableVertexAttribArray(mOutlineColourLocation); + + glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix.AsFloat()); + glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix.AsFloat()); + + glDrawElements(GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_SHORT, 0); + mIndices.clear(); + mVertices.clear(); +} + +PhysicsDebugRenderer::Vertex* PhysicsDebugRenderer::PushVertices(uint32_t vertexCount, uint32_t indexCount, const uint16_t* indices) +{ + auto base = mVertices.size(); + mVertices.resize(mVertices.size() + vertexCount); + mIndices.reserve(mIndices.size() + indexCount); + for(uint32_t i = 0; i < indexCount; ++i) + { + mIndices.push_back(base + indices[i]); + } + + return &mVertices[base]; +} + +PhysicsDebugRenderer::Vertex PhysicsDebugRenderer::MakeVertex(cpVect pos, float u, float v, float r, Vector4 fill, Vector4 outline) +{ + auto daliPos = mAdaptor.TranslateFromPhysicsSpace(Vector3((float)pos.x, (float)pos.y, 0.0f)); + return Vertex{Vector2(daliPos.x, daliPos.y), Vector2(u, v), r, fill, outline}; +} + +void PhysicsDebugRenderer::DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor) +{ + float r = (float)radius + mPointLineScale; + Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a); + Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a); + static const uint16_t indices[] = {0, 1, 2, 0, 2, 3}; + + Vertex* vertices = PushVertices(4, 6, indices); + + vertices[0] = MakeVertex(pos, -1, -1, r, fill, outline); + vertices[1] = MakeVertex(pos, -1, 1, r, fill, outline); + vertices[2] = MakeVertex(pos, 1, 1, r, fill, outline); + vertices[3] = MakeVertex(pos, 1, -1, r, fill, outline); + + DrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), 0.75f * radius)), outlineColor); +} + +void PhysicsDebugRenderer::DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color) +{ + DrawFatSegment(a, b, 0.0f, color, color); +} + +void PhysicsDebugRenderer::DrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor) +{ + static const uint16_t indices[] = {0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7}; + Vertex* vertices = PushVertices(8, 18, indices); + + cpVect t = cpvnormalize(cpvsub(b, a)); + + float r = (float)radius * mPointLineScale; + Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a); + Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a); + + vertices[0] = MakeVertex(a, (-t.x + t.y), (-t.x - t.y), r, fill, outline); + vertices[1] = MakeVertex(a, (-t.x - t.y), (+t.x - t.y), r, fill, outline); + vertices[2] = MakeVertex(a, (-0.0 + t.y), (-t.x + 0.0), r, fill, outline); + vertices[3] = MakeVertex(a, (-0.0 - t.y), (+t.x + 0.0), r, fill, outline); + vertices[4] = MakeVertex(a, (+0.0 + t.y), (-t.x - 0.0), r, fill, outline); + vertices[5] = MakeVertex(a, (+0.0 - t.y), (+t.x - 0.0), r, fill, outline); + vertices[6] = MakeVertex(a, (+t.x + t.y), (-t.x + t.y), r, fill, outline); + vertices[7] = MakeVertex(a, (+t.x - t.y), (+t.x + t.y), r, fill, outline); +} + +void PhysicsDebugRenderer::DrawPolygon(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor) +{ + Vector4 fill(fillColor.r, fillColor.g, fillColor.b, fillColor.a); + Vector4 outline(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a); + + std::vector indices; + for(int i = 0; i < count - 2; i++) + { + indices.push_back(0); + indices.push_back(4 * (i + 1)); + indices.push_back(4 * (i + 2)); + } + + // Polygon outline triangles. + for(int i0 = 0; i0 < count; i0++) + { + int i1 = (i0 + 1) % count; + indices.push_back(4 * i0 + 0); + indices.push_back(4 * i0 + 1); + indices.push_back(4 * i0 + 2); + indices.push_back(4 * i0 + 0); + indices.push_back(4 * i0 + 2); + indices.push_back(4 * i0 + 3); + indices.push_back(4 * i0 + 0); + indices.push_back(4 * i0 + 3); + indices.push_back(4 * i1 + 0); + indices.push_back(4 * i0 + 3); + indices.push_back(4 * i1 + 0); + indices.push_back(4 * i1 + 1); + } + + float inset = (float)-cpfmax(0, 2 * mPointLineScale - radius); + float outset = (float)radius + mPointLineScale; + float r = outset - inset; + + Vertex* vertices = PushVertices(4 * count, 3 * (5 * count - 2), &indices[0]); + for(int i = 0; i < count; i++) + { + cpVect v0 = verts[i]; + cpVect v_prev = verts[(i + (count - 1)) % count]; + cpVect v_next = verts[(i + (count + 1)) % count]; + + cpVect n1 = cpvnormalize(cpvrperp(cpvsub(v0, v_prev))); + cpVect n2 = cpvnormalize(cpvrperp(cpvsub(v_next, v0))); + cpVect of = cpvmult(cpvadd(n1, n2), 1.0 / (cpvdot(n1, n2) + 1.0f)); + cpVect v = cpvadd(v0, cpvmult(of, inset)); + + vertices[4 * i + 0] = MakeVertex(v, 0.0f, 0.0f, 0.0f, fill, outline); + vertices[4 * i + 1] = MakeVertex(v, (float)n1.x, (float)n1.y, r, fill, outline); + vertices[4 * i + 2] = MakeVertex(v, (float)of.x, (float)of.y, r, fill, outline); + vertices[4 * i + 3] = MakeVertex(v, (float)n2.x, (float)n2.y, r, fill, outline); + } +} + +void PhysicsDebugRenderer::DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color) +{ + float r = (float)(size * 0.5f * mPointLineScale); + Vector4 fill(color.r, color.g, color.b, color.a); + static const uint16_t indices[] = {0, 1, 2, 0, 2, 3}; + Vertex* vertex = PushVertices(4, 6, indices); + vertex[0] = MakeVertex(pos, -1, -1, r, fill, fill); + vertex[1] = MakeVertex(pos, -1, 1, r, fill, fill); + vertex[2] = MakeVertex(pos, 1, 1, r, fill, fill); + vertex[3] = MakeVertex(pos, 1, -1, r, fill, fill); +} + +cpSpaceDebugColor PhysicsDebugRenderer::DrawColorForShape(cpShape* shape) +{ + static cpSpaceDebugColor Colors[] = { + {0xb5 / 255.0f, 0x89 / 255.0f, 0x00 / 255.0f, 1.0f}, + {0xcb / 255.0f, 0x4b / 255.0f, 0x16 / 255.0f, 1.0f}, + {0xdc / 255.0f, 0x32 / 255.0f, 0x2f / 255.0f, 1.0f}, + {0xd3 / 255.0f, 0x36 / 255.0f, 0x82 / 255.0f, 1.0f}, + {0x6c / 255.0f, 0x71 / 255.0f, 0xc4 / 255.0f, 1.0f}, + {0x26 / 255.0f, 0x8b / 255.0f, 0xd2 / 255.0f, 1.0f}, + {0x2a / 255.0f, 0xa1 / 255.0f, 0x98 / 255.0f, 1.0f}, + {0x85 / 255.0f, 0x99 / 255.0f, 0x00 / 255.0f, 1.0f}, + }; + + if(cpShapeGetSensor(shape)) + { + return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 0.1f}; + } + else + { + cpBody* body = cpShapeGetBody(shape); + + if(cpBodyIsSleeping(body)) + { + return cpSpaceDebugColor{0x58 / 255.0f, 0x6e / 255.0f, 0x75 / 255.0f, 1.0f}; + } + else if(cpBodyIsSleepThresholdExceeded(body, shape)) + { + return cpSpaceDebugColor{0x93 / 255.0f, 0xa1 / 255.0f, 0xa1 / 255.0f, 1.0f}; + } + else + { + uint32_t val = (uint32_t)cpShapeGetHashId(shape); + + // scramble the bits up using Robert Jenkins' 32 bit integer hash function + val = (val + 0x7ed55d16) + (val << 12); + val = (val ^ 0xc761c23c) ^ (val >> 19); + val = (val + 0x165667b1) + (val << 5); + val = (val + 0xd3a2646c) ^ (val << 9); + val = (val + 0xfd7046c5) + (val << 3); + val = (val ^ 0xb55a4f09) ^ (val >> 16); + + return Colors[val & 0x7]; + } + } + + return cpSpaceDebugColor{1.0f, 1.0f, 1.0f, 1.0f}; +} + +} // namespace Dali::Toolkit::Physics::Internal diff --git a/dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.h b/dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.h new file mode 100644 index 0000000..2a4335b --- /dev/null +++ b/dali-physics/internal/chipmunk-impl/chipmunk-physics-debug-renderer.h @@ -0,0 +1,126 @@ +#pragma once + +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +using Dali::Actor; +using Dali::CameraActor; +using Dali::Geometry; +using Dali::Renderer; +using Dali::Shader; +using Dali::TextureSet; + +namespace Dali::Toolkit::Physics::Internal +{ +class PhysicsAdaptor; + +class PhysicsDebugRenderer +{ +public: + // Creates and initializes a new renderer + static std::unique_ptr New(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor); + /** + * Get the callback (for actor creation) + */ + + std::unique_ptr& GetCallback() + { + return mRenderCallback; + } + + void UpdateWindowSize(Dali::Vector2 size); + + /** + * Constructor. + * @param[in] width Width of the renderer - viewport + * @param[in] height Height of the renderer - viewport + */ + PhysicsDebugRenderer(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor); + + /** + * Get the drawing options struct ( construct only ) + */ + const cpSpaceDebugDrawOptions& GetDebugDrawOptions() + { + return mDebugDrawOptions; + } + + struct Vertex + { + Dali::Vector2 position; + Dali::Vector2 uvs; + float radius; + Dali::Vector4 fillColor; + Dali::Vector4 outlineColor; + }; + +public: // Debug functions (Creates indices & verts) + void DrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor); + void DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color); + void DrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor); + void DrawPolygon(int count, const cpVect* verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor); + void DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color); + cpSpaceDebugColor DrawColorForShape(cpShape* shape); + +private: + bool OnRender(const Dali::RenderCallbackInput& input); + void Setup(); + void PrepareShader(); + void RenderLines(const Dali::RenderCallbackInput& input); + + Vertex* PushVertices(uint32_t vertexCount, uint32_t indexCount, const uint16_t* indices); + Vertex MakeVertex(cpVect pos, float u, float v, float r, Vector4 fill, Vector4 outline); + +private: + CameraActor mCamera; + cpSpaceDebugDrawOptions mDebugDrawOptions; + Renderer mDebugRenderer; + std::unique_ptr mRenderCallback; + + enum class State + { + INIT, + RENDER + } mState{State::INIT}; + + std::vector mVertices; + std::vector mIndices; + + Dali::Matrix mModelViewMatrix; + Dali::Matrix mViewMatrix; + Dali::Matrix mProjectionMatrix; + int mWidth; + int mHeight; + PhysicsAdaptor& mAdaptor; + + float mPointLineScale{2.0f}; + GLint mPositionLocation; + GLint mUvsLocation; + GLint mRadiusLocation; + GLint mFillColourLocation; + GLint mOutlineColourLocation; + GLint mProjectionLocation; + GLint mModelViewLocation; + GLuint mIndexBufferId; + GLuint mVertexBufferId; + GLuint mProgramId; +}; + +} // namespace Dali::Toolkit::Physics::Internal diff --git a/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.cpp b/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.cpp index c909474..7423edd 100644 --- a/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.cpp +++ b/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.cpp @@ -112,6 +112,14 @@ void ChipmunkPhysicsWorld::Integrate(float timestep) { cpSpaceStep(mSpace, timestep); } + + if(mPhysicsDebugState == Physics::PhysicsAdaptor::DebugState::ON) + { + if(mDebugRenderer) + { + cpSpaceDebugDraw(mSpace, const_cast(&mDebugRenderer->GetDebugDrawOptions())); + } + } } Dali::Any ChipmunkPhysicsWorld::HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Any nativeFilter, Dali::Vector3& localPivot, float& distanceFromCamera) diff --git a/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.h b/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.h index 39918d2..10ba5e8 100644 --- a/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.h +++ b/dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.h @@ -19,6 +19,7 @@ #include #include +#include #include namespace Dali::Toolkit::Physics::Internal @@ -39,8 +40,27 @@ public: Dali::Any HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Any nativeFilter, Dali::Vector3& localPivot, float& distanceFromCamera) override; + /** + * Set the debug renderer. PhysicsWorld will take ownership + */ + void SetDebugRenderer(PhysicsDebugRenderer* renderer) + { + mDebugRenderer.reset(renderer); + } + + PhysicsDebugRenderer& GetDebugRenderer() + { + return *mDebugRenderer.get(); + } + + bool HasDebugRenderer() + { + return mDebugRenderer.get() != nullptr; + } + private: - cpSpace* mSpace{nullptr}; + cpSpace* mSpace{nullptr}; + std::unique_ptr mDebugRenderer; }; } //namespace Dali::Toolkit::Physics::Internal diff --git a/dali-physics/internal/file.list b/dali-physics/internal/file.list index 87c7cb2..19137dd 100644 --- a/dali-physics/internal/file.list +++ b/dali-physics/internal/file.list @@ -5,6 +5,7 @@ set(physics3d_internal_dir ${physics_internal_dir}/bullet-impl) set(physics2d_src_files ${physics_src_files} ${physics2d_internal_dir}/chipmunk-physics-actor-impl.cpp ${physics2d_internal_dir}/chipmunk-physics-adaptor-impl.cpp + ${physics2d_internal_dir}/chipmunk-physics-debug-renderer.cpp ${physics2d_internal_dir}/chipmunk-physics-world-impl.cpp ${physics_internal_dir}/physics-adaptor-impl.cpp ${physics_internal_dir}/physics-world-impl.cpp diff --git a/dali-physics/third-party/chipmunk2d/include/chipmunk/cpBody.h b/dali-physics/third-party/chipmunk2d/include/chipmunk/cpBody.h index 7e6943d..8e46cc3 100644 --- a/dali-physics/third-party/chipmunk2d/include/chipmunk/cpBody.h +++ b/dali-physics/third-party/chipmunk2d/include/chipmunk/cpBody.h @@ -76,6 +76,9 @@ CP_EXPORT void cpBodySleepWithGroup(cpBody *body, cpBody *group); /// Returns true if the body is sleeping. CP_EXPORT cpBool cpBodyIsSleeping(const cpBody *body); +// Returns true if the body is exceeding the sleep threshold +CP_EXPORT cpBool cpBodyIsSleepThresholdExceeded(const cpBody *body, const cpShape *shape); + /// Get the type of the body. CP_EXPORT cpBodyType cpBodyGetType(cpBody *body); /// Set the type of the body. diff --git a/dali-physics/third-party/chipmunk2d/include/chipmunk/cpShape.h b/dali-physics/third-party/chipmunk2d/include/chipmunk/cpShape.h index c78ed05..2b04062 100644 --- a/dali-physics/third-party/chipmunk2d/include/chipmunk/cpShape.h +++ b/dali-physics/third-party/chipmunk2d/include/chipmunk/cpShape.h @@ -79,6 +79,9 @@ CP_EXPORT void cpShapeDestroy(cpShape *shape); /// Destroy and Free a shape. CP_EXPORT void cpShapeFree(cpShape *shape); +/// Get the hash ID of the shape +CP_EXPORT cpHashValue cpShapeGetHashId(cpShape* shape); + /// Update, cache and return the bounding box of a shape based on the body it's attached to. CP_EXPORT cpBB cpShapeCacheBB(cpShape *shape); /// Update, cache and return the bounding box of a shape with an explicit transformation. diff --git a/dali-physics/third-party/chipmunk2d/src/CMakeLists.txt b/dali-physics/third-party/chipmunk2d/src/CMakeLists.txt index 34b30eb..55f7954 100644 --- a/dali-physics/third-party/chipmunk2d/src/CMakeLists.txt +++ b/dali-physics/third-party/chipmunk2d/src/CMakeLists.txt @@ -39,6 +39,12 @@ if(ANDROID) FIND_LIBRARY(LOGLIB log) endif(ANDROID) +# If you want to reduce the size of the library slightly, uncomment this section +# +#if(NOT ENABLE_DEBUG) +# add_definitions( "-DCP_SPACE_DISABLE_DEBUG_API") +#endif() + if(BUILD_SHARED) add_library(chipmunk SHARED ${chipmunk_source_files} @@ -117,4 +123,3 @@ if (ENABLE_PKG_CONFIGURE) DESTINATION ${LIB_DIR}/pkgconfig ) endif() - diff --git a/dali-physics/third-party/chipmunk2d/src/cpBody.c b/dali-physics/third-party/chipmunk2d/src/cpBody.c index 8ad2bc9..e5caaf9 100644 --- a/dali-physics/third-party/chipmunk2d/src/cpBody.c +++ b/dali-physics/third-party/chipmunk2d/src/cpBody.c @@ -133,6 +133,12 @@ cpBodyIsSleeping(const cpBody *body) return (body->sleeping.root != ((cpBody*)0)); } +cpBool +cpBodyIsSleepThresholdExceeded(const cpBody *body, const cpShape *shape) +{ + return body->sleeping.idleTime > shape->space->sleepTimeThreshold; +} + cpBodyType cpBodyGetType(cpBody *body) { diff --git a/dali-physics/third-party/chipmunk2d/src/cpShape.c b/dali-physics/third-party/chipmunk2d/src/cpShape.c index 513b535..942bfff 100644 --- a/dali-physics/third-party/chipmunk2d/src/cpShape.c +++ b/dali-physics/third-party/chipmunk2d/src/cpShape.c @@ -109,6 +109,12 @@ cpFloat cpShapeGetMoment(cpShape *shape){ return shape->massInfo.m*shape->massIn cpFloat cpShapeGetArea(cpShape *shape){ return shape->massInfo.area; } cpVect cpShapeGetCenterOfGravity(cpShape *shape) { return shape->massInfo.cog; } +cpHashValue +cpShapeGetHashId(cpShape* shape) +{ + return shape->hashid; +} + cpBB cpShapeGetBB(const cpShape *shape) {