Skip to content

Commit 37b90e7

Browse files
authored
Merge pull request #116 from scratchcpp/touching_object
Implement collision detection (touching sprite, point)
2 parents 0461bba + a017e1a commit 37b90e7

24 files changed

+885
-237
lines changed

libscratchcpp

Submodule libscratchcpp updated 111 files

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ qt_add_qml_module(scratchcpp-render
7070
textbubbleshape.h
7171
textbubblepainter.cpp
7272
textbubblepainter.h
73+
cputexturemanager.cpp
74+
cputexturemanager.h
7375
blocks/penextension.cpp
7476
blocks/penextension.h
7577
blocks/penblocks.cpp

src/ProjectPlayer.qml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,53 @@ ProjectScene {
156156
}
157157
}
158158

159+
// Uncomment to display sprite bounding boxes (for debugging)
160+
/*Rectangle {
161+
function translateX(x) {
162+
// Translates Scratch X-coordinate to the scene coordinate system
163+
return root.stageScale * (root.stageWidth / 2 + x)
164+
}
165+
166+
function translateY(y) {
167+
// Translates Scratch Y-coordinate to the scene coordinate system
168+
return root.stageScale * (root.stageHeight / 2 - y)
169+
}
170+
171+
id: boundRect
172+
color: "transparent"
173+
border.color: "red"
174+
border.width: 3
175+
visible: targetItem.visible
176+
177+
function updatePosition() {
178+
let bounds = targetItem.getQmlBounds();
179+
boundRect.x = translateX(bounds.left);
180+
boundRect.y = translateY(bounds.top);
181+
width = bounds.width * root.stageScale;
182+
height = -bounds.height * root.stageScale;
183+
}
184+
185+
Connections {
186+
target: targetItem
187+
188+
function onXChanged() { boundRect.updatePosition() }
189+
function onYChanged() { boundRect.updatePosition() }
190+
function onRotationChanged() { boundRect.updatePosition() }
191+
function onWidthChanged() { boundRect.updatePosition() }
192+
function onHeightChanged() { boundRect.updatePosition() }
193+
function onScaleChanged() { boundRect.updatePosition() }
194+
}
195+
196+
Connections {
197+
property Scale transform: Scale {}
198+
target: transform
199+
200+
function onXScaleChanged() { boundRect.updatePosition() }
201+
202+
Component.onCompleted: transform = targetItem.transform[0]
203+
}
204+
}*/
205+
159206
Loader {
160207
readonly property alias model: targetItem.spriteModel
161208
active: model ? model.bubbleText !== "" : false

src/cputexturemanager.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#include "cputexturemanager.h"
4+
#include "texture.h"
5+
6+
using namespace scratchcpprender;
7+
8+
CpuTextureManager::CpuTextureManager()
9+
{
10+
}
11+
12+
CpuTextureManager::~CpuTextureManager()
13+
{
14+
for (const auto &[handle, data] : m_textureData)
15+
delete[] data;
16+
}
17+
18+
GLubyte *CpuTextureManager::getTextureData(const Texture &texture)
19+
{
20+
if (!texture.isValid())
21+
return nullptr;
22+
23+
const GLuint handle = texture.handle();
24+
auto it = m_textureData.find(handle);
25+
26+
if (it == m_textureData.cend()) {
27+
if (addTexture(texture))
28+
return m_textureData[handle];
29+
else
30+
return nullptr;
31+
} else
32+
return it->second;
33+
}
34+
35+
const std::vector<QPoint> &CpuTextureManager::getTextureConvexHullPoints(const Texture &texture)
36+
{
37+
static const std::vector<QPoint> empty;
38+
39+
if (!texture.isValid())
40+
return empty;
41+
42+
const GLuint handle = texture.handle();
43+
auto it = m_convexHullPoints.find(handle);
44+
45+
if (it == m_convexHullPoints.cend()) {
46+
if (addTexture(texture))
47+
return m_convexHullPoints[handle];
48+
else
49+
return empty;
50+
} else
51+
return it->second;
52+
}
53+
54+
bool CpuTextureManager::addTexture(const Texture &texture)
55+
{
56+
if (!texture.isValid())
57+
return false;
58+
59+
const GLuint handle = texture.handle();
60+
const int width = texture.width();
61+
const int height = texture.height();
62+
63+
QOpenGLFunctions glF;
64+
glF.initializeOpenGLFunctions();
65+
66+
// Create a FBO for the texture
67+
unsigned int fbo;
68+
glF.glGenFramebuffers(1, &fbo);
69+
glF.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
70+
glF.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, handle, 0);
71+
72+
if (glF.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
73+
qWarning() << "error: framebuffer incomplete (CpuTextureManager)";
74+
glF.glDeleteFramebuffers(1, &fbo);
75+
return false;
76+
}
77+
78+
// Read pixels
79+
GLubyte *pixels = new GLubyte[width * height * 4]; // 4 channels (RGBA)
80+
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
81+
82+
// Flip vertically
83+
int rowSize = width * 4;
84+
GLubyte *tempRow = new GLubyte[rowSize];
85+
86+
for (size_t i = 0; i < height / 2; ++i) {
87+
size_t topRowIndex = i * rowSize;
88+
size_t bottomRowIndex = (height - 1 - i) * rowSize;
89+
90+
// Swap rows
91+
memcpy(tempRow, &pixels[topRowIndex], rowSize);
92+
memcpy(&pixels[topRowIndex], &pixels[bottomRowIndex], rowSize);
93+
memcpy(&pixels[bottomRowIndex], tempRow, rowSize);
94+
}
95+
96+
delete[] tempRow;
97+
98+
m_textureData[handle] = pixels;
99+
m_convexHullPoints[handle] = {};
100+
std::vector<QPoint> &hullPoints = m_convexHullPoints[handle];
101+
102+
// Get convex hull points
103+
for (int y = 0; y < height; y++) {
104+
for (int x = 0; x < width; x++) {
105+
int index = (y * width + x) * 4; // 4 channels (RGBA)
106+
107+
// Check alpha channel
108+
if (pixels[index + 3] > 0)
109+
hullPoints.push_back(QPoint(x, y));
110+
}
111+
}
112+
113+
// Cleanup
114+
glF.glBindFramebuffer(GL_FRAMEBUFFER, 0);
115+
glF.glDeleteFramebuffers(1, &fbo);
116+
117+
return true;
118+
}

src/cputexturemanager.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#pragma once
4+
5+
#include <QPoint>
6+
#include <QtOpenGL>
7+
#include <unordered_map>
8+
9+
namespace scratchcpprender
10+
{
11+
12+
class Texture;
13+
14+
class CpuTextureManager
15+
{
16+
public:
17+
CpuTextureManager();
18+
~CpuTextureManager();
19+
20+
GLubyte *getTextureData(const Texture &texture);
21+
const std::vector<QPoint> &getTextureConvexHullPoints(const Texture &texture);
22+
23+
private:
24+
bool addTexture(const Texture &texture);
25+
26+
std::unordered_map<GLuint, GLubyte *> m_textureData;
27+
std::unordered_map<GLuint, std::vector<QPoint>> m_convexHullPoints;
28+
};
29+
30+
} // namespace scratchcpprender

src/internal/TextBubble.qml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ TextBubbleShape {
8585
function onScaleChanged() { positionBubble() }
8686
}
8787

88+
Connections {
89+
property Scale transform: Scale {}
90+
target: transform
91+
92+
function onXScaleChanged() { positionBubble() }
93+
94+
Component.onCompleted: transform = root.target.transform[0]
95+
}
96+
8897
Text {
8998
id: bubbleText
9099
anchors.left: parent.left

src/irenderedtarget.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Texture;
2323
class IRenderedTarget : public QNanoQuickItem
2424
{
2525
public:
26-
IRenderedTarget(QNanoQuickItem *parent = nullptr) :
26+
IRenderedTarget(QQuickItem *parent = nullptr) :
2727
QNanoQuickItem(parent)
2828
{
2929
}
@@ -83,8 +83,11 @@ class IRenderedTarget : public QNanoQuickItem
8383
virtual void setGraphicEffect(ShaderManager::Effect effect, double value) = 0;
8484
virtual void clearGraphicEffects() = 0;
8585

86-
virtual void updateHullPoints(QOpenGLFramebufferObject *fbo) = 0;
87-
virtual const std::vector<QPointF> &hullPoints() const = 0;
86+
virtual const std::vector<QPoint> &hullPoints() const = 0;
87+
88+
virtual bool containsScratchPoint(double x, double y) const = 0;
89+
90+
virtual bool touchingClones(const std::vector<libscratchcpp::Sprite *> &clones) const = 0;
8891
};
8992

9093
} // namespace scratchcpprender

src/projectloader.cpp

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ using namespace libscratchcpp;
2020
ProjectLoader::ProjectLoader(QObject *parent) :
2121
QObject(parent)
2222
{
23-
m_project.setDownloadProgressCallback([this](unsigned int finished, unsigned int all) {
23+
m_project.downloadProgressChanged().connect([this](unsigned int finished, unsigned int all) {
2424
if (finished != m_downloadedAssets) {
2525
m_downloadedAssets = finished;
2626
emit downloadedAssetsChanged();
@@ -208,12 +208,8 @@ void ProjectLoader::stop()
208208

209209
void ProjectLoader::answerQuestion(const QString &answer)
210210
{
211-
if (m_engine) {
212-
auto f = m_engine->questionAnswered();
213-
214-
if (f)
215-
f(answer.toStdString());
216-
}
211+
if (m_engine)
212+
m_engine->questionAnswered()(answer.toStdString());
217213
}
218214

219215
void ProjectLoader::timerEvent(QTimerEvent *event)
@@ -255,16 +251,11 @@ void ProjectLoader::load()
255251
m_engine->setCloneLimit(m_cloneLimit);
256252
m_engine->setSpriteFencingEnabled(m_spriteFencing);
257253

258-
auto redrawHandler = std::bind(&ProjectLoader::redraw, this);
259-
m_engine->setRedrawHandler(std::function<void()>(redrawHandler));
260-
261-
auto addMonitorHandler = std::bind(&ProjectLoader::addMonitor, this, std::placeholders::_1);
262-
m_engine->setAddMonitorHandler(std::function<void(Monitor *)>(addMonitorHandler));
263-
264-
auto removeMonitorHandler = std::bind(&ProjectLoader::removeMonitor, this, std::placeholders::_1, std::placeholders::_2);
265-
m_engine->setRemoveMonitorHandler(std::function<void(Monitor *, IMonitorHandler *)>(removeMonitorHandler));
254+
m_engine->aboutToRender().connect(&ProjectLoader::redraw, this);
255+
m_engine->monitorAdded().connect(&ProjectLoader::addMonitor, this);
256+
m_engine->monitorRemoved().connect(&ProjectLoader::removeMonitor, this);
266257

267-
m_engine->setQuestionAsked([this](const std::string &question) { emit questionAsked(QString::fromStdString(question)); });
258+
m_engine->questionAsked().connect([this](const std::string &question) { emit questionAsked(QString::fromStdString(question)); });
268259

269260
// Load targets
270261
const auto &targets = m_engine->targets();

0 commit comments

Comments
 (0)