반응형

SDL에 2D 카메라를 만들어 보자.

 

character.bmp
0.00MB
shop.bmp
0.03MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "SDL.h"
 
class Camera {
public:
    int m_width;
    int m_height;
    int m_cameraBorder;
    int m_target_width;
    int m_target_height;
    float m_x, m_y;
    SDL_Texture* m_targetTexture;
public:
    Camera(SDL_Renderer* renderer, int width, int height, int border, SDL_BlendMode mode) {
        m_width = width;
        m_height = height;
        m_cameraBorder = border;
        m_target_width = width + border * 2;
        m_target_height = height + border * 2;
        m_x = 0;
        m_y = 0;
        m_targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET,
            m_target_width, m_target_height);
        SDL_SetTextureBlendMode(m_targetTexture, mode);
    }
    void Draw(SDL_Renderer* renderer, int windowHeight) {
        float pixel_h = (float)windowHeight / m_height;
        float correction_x = (int)m_x - m_x;
        float correction_y = (int)m_y - m_y;
        SDL_Rect dst = { (int)(correction_x * pixel_h - pixel_h * m_cameraBorder),
            (int)(correction_y * pixel_h - pixel_h * m_cameraBorder),
            (int)(m_target_width * pixel_h),
            (int)(m_target_height * pixel_h) };
        SDL_RenderCopy(renderer, m_targetTexture, NULL&dst);
    }
    void Update(int xDirection, int yDirection) {
        m_x += 0.5f * xDirection;
        m_y += 0.5f * yDirection;
    }
    void CameraReset(SDL_Renderer* renderer, int width, int height, int border, SDL_BlendMode mode) {
        m_width = width;
        m_height = height;
        m_cameraBorder = border;
        m_target_width = width + border * 2;
        m_target_height = height + border * 2;
        SDL_DestroyTexture(m_targetTexture);
        m_targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET,
            m_target_width, m_target_height);
        SDL_SetTextureBlendMode(m_targetTexture, mode);
    }
    ~Camera() {
        SDL_DestroyTexture(m_targetTexture);
    }
};
 
class Sprite {
public:
    int width;
    int height;
    float m_destX, m_destY;
    SDL_Surface* surface;
    SDL_Texture* texture;
public:
    Sprite(SDL_Renderer* renderer, const char* filename) {
        surface = SDL_LoadBMP(filename);
        width = surface->w;
        height = surface->h;
        m_destX = 0;
        m_destY = 0;
        texture = SDL_CreateTextureFromSurface(renderer, surface);
        SDL_FreeSurface(surface);
    }
    void DrawSprite(SDL_Renderer* renderer, Camera* camera, int x, int y) {
        SDL_Rect destRect = { (int)(m_destX - camera->m_x) + camera->m_cameraBorder + x,
                (int)(m_destY - camera->m_y) + camera->m_cameraBorder + y,
                width, height };
 
        SDL_RenderCopy(renderer, texture, NULL&destRect);
    }
    ~Sprite() {
        SDL_DestroyTexture(texture);
    }
};
 
int main(int argc, char** argv) {
    const int Window_Width = 640;
    const int Window_Height = 480;
 
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
    SDL_Window* window = SDL_CreateWindow("SDL Simple Camera", SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED, Window_Width, Window_Height, SDL_WINDOW_RESIZABLE);
    SDL_assert(window);
 
    SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED |
        SDL_RENDERER_PRESENTVSYNC);
    SDL_assert(renderer);
 
    // Init
    Camera camera = Camera(renderer, 1601201, SDL_BLENDMODE_BLEND);
    SDL_assert(camera.m_targetTexture);
    Sprite shopSprite = Sprite(renderer, "shop.bmp");
    Sprite characterSprite = Sprite(renderer, "character.bmp");
 
    int xDirection = 0;
    int yDirection = 0;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                if (event.key.keysym.sym == SDLK_LEFT)
                    xDirection = -1;
                if (event.key.keysym.sym == SDLK_RIGHT)
                    xDirection = 1;
                if (event.key.keysym.sym == SDLK_UP)
                    yDirection = -1;
                if (event.key.keysym.sym == SDLK_DOWN)
                    yDirection = 1;
                if (event.key.keysym.sym == SDLK_r)
                    camera.CameraReset(renderer, 1601201, SDL_BLENDMODE_BLEND);
                if (event.key.keysym.sym == SDLK_e)
                    camera.CameraReset(renderer, 6404801, SDL_BLENDMODE_BLEND);
                break;
 
            default:
                xDirection = 0;
                yDirection = 0;
                break;
            }
        }
        camera.Update(xDirection, yDirection);
 
        // Camera target
        SDL_SetRenderTarget(renderer, camera.m_targetTexture);
        SDL_SetRenderDrawColor(renderer, 0000xFF);
        SDL_RenderClear(renderer);
 
        // Draw Sprite
        shopSprite.DrawSprite(renderer, &camera, 00);
        characterSprite.DrawSprite(renderer, &camera, 11666);
 
        // Screen target
        SDL_SetRenderTarget(renderer, NULL);
        SDL_SetRenderDrawColor(renderer, 0x000xFF0x000xFF);
        SDL_RenderClear(renderer);
 
        camera.Draw(renderer, Window_Height);
 
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

실행하면 shop과 character 스프라이트가 표시된다.

 

r, e 키로 해상도를 바꿀 수 있다.

 

방향키로 카메라를 움직일 수 있다.

 

 

※ 참고

Pixel perfect camera in SDL2

 

반응형
Posted by J-sean
:
반응형

오브젝트가 충돌한 포인트를 확인해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
class DebugDrawer : public b2Draw
{
public:
    DebugDrawer(SDL_Renderer* renderer) {
        this->renderer = renderer;
    }
private:
    SDL_Renderer* renderer;
    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawPoint(const b2Vec2& p, float sizeconst b2Color& color) {}
    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
        SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
        SDL_FRect* rect = new SDL_FRect{ vertices[0].x, vertices[0].y, vertices[1].x
            - vertices[0].x, vertices[3].y - vertices[0].y };
        SDL_RenderDrawRectF(renderer, rect);
    }
    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) {}
    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) {}
    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {}
    void DrawTransform(const b2Transform& xf) {}
};
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // DebugDraw 정의
    DebugDrawer* debugDrawer = new DebugDrawer(renderer);
    debugDrawer->SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit |
        b2Draw::e_centerOfMassBit | b2Draw::e_aabbBit | b2Draw::e_pairBit);
    world.SetDebugDraw(debugDrawer);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
 
        // DebugDraw 그리기
        world.DebugDraw();
 
        // 가장 마지막에 contact point 그리기
        SDL_SetRenderDrawColor(renderer, 02550, SDL_ALPHA_OPAQUE);
        for (b2Contact* contact = world.GetContactList(); contact; contact = contact->GetNext()) {
            for (int i = 0; i < contact->GetManifold()->pointCount; i++)
            {
                b2Vec2 point = contact->GetFixtureA()->GetBody()->GetWorldPoint(
                    contact->GetManifold()->points[i].localPoint);
                SDL_FRect rect = { point.x - 5, point.y - 51010 };
                SDL_RenderFillRectF(renderer, &rect);
            }
        }
        // A very important point to note if you do this, is that the existence of a contact in these
        // lists does not mean that the two fixtures of the contact are actually touching - it only means
        // their AABBs are touching. If you want to know if the fixtures themselves are really touching
        // you can use IsTouching() to check.
 
        SDL_RenderPresent(renderer);
    }
 
    delete debugDrawer;
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

충돌 포인트가 녹색 사각형으로 표시된다.

 

완전히 멈추면 두 개의 포인트가 표시된다.

 

 

이제 검은색으로 법선 벡터 그리는 코드를 추가하자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 가장 마지막에 contact point & 법선 벡터 그리기
for (b2Contact* contact = world.GetContactList(); contact; contact = contact->GetNext()) {
    for (int i = 0; i < contact->GetManifold()->pointCount; i++)
    {
        b2Vec2 point = contact->GetFixtureA()->GetBody()->GetWorldPoint(
            contact->GetManifold()->points[i].localPoint);
        SDL_FRect rect = { point.x - 5, point.y - 51010 };
        SDL_SetRenderDrawColor(renderer, 02550, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRectF(renderer, &rect);
 
        SDL_SetRenderDrawColor(renderer, 000, SDL_ALPHA_OPAQUE);
        SDL_RenderDrawLineF(renderer, point.x, point.y,
            point.x + contact->GetManifold()->localNormal.x * 20, point.y + contact->GetManifold()->localNormal.y * 20);
    }
}
// A very important point to note if you do this, is that the existence of a contact in these
// lists does not mean that the two fixtures of the contact are actually touching - it only means
// their AABBs are touching. If you want to know if the fixtures themselves are really touching
// you can use IsTouching() to check.
 

 

코드를 수정하고 빌드한다.

 

녹색 충돌 포인트 위에 검은색 법선 벡터가 그려진다.

 

 

 

속도(velocity) 벡터는 아래와 같은 코드로 표시할 수 있다.

 

1
2
3
4
SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
b2Vec2 position = playerBody->GetPosition();
SDL_RenderDrawLineF(renderer, position.x, position.y,
    position.x + playerBody->GetLinearVelocity().x * 10, position.y + playerBody->GetLinearVelocity().y * 10);
 

 

 

 

 

※ 참고

Box2D b2ContactListener

 

반응형
Posted by J-sean
:
반응형

한 오브젝트에 여러개의 Shape을 추가해 충돌을 감지해 보자.

XXXShape이나 FixtureDef를 CreateFixture()에 전달하면 충돌 감지기가 추가된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
class DebugDrawer : public b2Draw
{
public:
    DebugDrawer(SDL_Renderer* renderer) {
        this->renderer = renderer;
    }
private:
    SDL_Renderer* renderer;
    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawPoint(const b2Vec2& p, float sizeconst b2Color& color) {}
    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
        SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
        SDL_FRect* rect = new SDL_FRect{ vertices[0].x, vertices[0].y, vertices[1].x
            - vertices[0].x, vertices[3].y - vertices[0].y };
        SDL_RenderDrawRectF(renderer, rect);
    }
    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) {}
    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) {}
    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {}
    void DrawTransform(const b2Transform& xf) {}
};
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // DebugDraw 정의
    DebugDrawer* debugDrawer = new DebugDrawer(renderer);
    debugDrawer->SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit |
        b2Draw::e_centerOfMassBit | b2Draw::e_aabbBit | b2Draw::e_pairBit);
    world.SetDebugDraw(debugDrawer);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
    // Player에 shape 추가
    b2PolygonShape another;
    another.SetAsBox(1040, b2Vec2(0-60), 0.0f);
    playerBody->CreateFixture(&another, 1.0f);
    // Creates a fixture from a shape and attach it to this body. This is a convenience function.
    // Use b2FixtureDef if you need to set parameters like friction, restitution, user data, or
    // filtering. If the density is non-zero, this function automatically updates the mass of the body.
    /*
    b2Fixture* temp = NULL;
    for (b2Fixture* fix = playerBody->GetFixtureList(); fix; fix=fix->GetNext()) {
        std::cout << fix->GetFriction() << std::endl;
        if (fix->GetFriction() == 0.2f)
            temp = fix;
        // 여기서 바로 playerBody->DestroyFixture()를 호출하면 fix가 파괴되어 GetNext()를 호출 할 수
        // 없게 된다. 임시 변수에 저장하고 for문을 벗어나면 파괴하자.
    }
    if (temp != NULL)
        playerBody->DestroyFixture(temp);
    // 이 예제에서는 Friction값을 비교해서 삭제할 Fixture를 선택했지만 필요하다면
    // UserData를 설정해서 비교하자.
    */
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
 
        // 가장 마지막에 DebugDraw 그리기
        world.DebugDraw();
 
        SDL_RenderPresent(renderer);
    }
 
    delete debugDrawer;
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

실행하면 공룡의 뿔같은 충돌 감지기가 추가된 모습이 보인다.

 

뿔 처럼 생긴 충돌 감지기는 크기, 위치, Density만 설정되어있다.

 

반응형
Posted by J-sean
:
반응형

Box2D DebugDraw를 사용해 보자.

 

b2Draw 클래스를 상속해 DebugDraw 클래스를 만들고 사용하면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
class DebugDrawer : public b2Draw
{
public:
    DebugDrawer(SDL_Renderer* renderer) {
        this->renderer = renderer;
    }
private:
    SDL_Renderer* renderer;
    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawPoint(const b2Vec2& p, float sizeconst b2Color& color) {}
    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
        SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
        SDL_FRect* rect = new SDL_FRect{ vertices[0].x, vertices[0].y, vertices[1].x
            - vertices[0].x, vertices[3].y - vertices[0].y };
        SDL_RenderDrawRectF(renderer, rect);
    }
    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) {}
    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) {}
    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {}
    void DrawTransform(const b2Transform& xf) {}
};
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // DebugDraw 정의
    DebugDrawer* debugDrawer = new DebugDrawer(renderer);
    debugDrawer->SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit |
        b2Draw::e_centerOfMassBit | b2Draw::e_aabbBit | b2Draw::e_pairBit);
    world.SetDebugDraw(debugDrawer);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
 
        // 가장 마지막에 DebugDraw 그리기
        world.DebugDraw();
 
        SDL_RenderPresent(renderer);
    }
 
    // debugDrawer 해제
    delete debugDrawer;
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

위 GIF 이미지에서 잘 보이지 않지만 모든 오브젝트의 Shape에 빨간 폴리곤이 그려진다.

 

실제로는 빨간 폴리곤이 잘 보인다.

 

※ 참고

Box2D b2Draw

 

반응형
Posted by J-sean
:
반응형

충돌을 감지하고 어떤 오브젝트가 충돌했는지 확인해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
    // Player 유저 정보
    const char* playerName = "Player";
    playerBody->GetUserData().pointer = reinterpret_cast<uintptr_t>(playerName);
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
    // Ground 유저 정보
    const char* groundName = "Ground";
    groundBody->GetUserData().pointer = reinterpret_cast<uintptr_t>(groundName);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
    // Wall 유저 정보
    const char* wallName = "Wall";
    wallBody->GetUserData().pointer = reinterpret_cast<uintptr_t>(wallName);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        for (b2Contact* c = world.GetContactList(); c; c = c->GetNext())
        {
            if (c->IsTouching()) {
                printf("FixtureA: %s\n", (char*)(c->GetFixtureA()->GetBody()->GetUserData().pointer));
                printf("FixtureB: %s\n", (char*)(c->GetFixtureB()->GetBody()->GetUserData().pointer));
                printf("Count: %d\n", c->GetManifold()->pointCount);
            }
            //if (c->GetFixtureA()->IsSensor() == false)
                //c->GetFixtureA()->SetSensor(true);
        }
        // A very important point to note if you do this, is that the existence of a contact in these
        // lists does not mean that the two fixtures of the contact are actually touching - it only
        // means their AABBs are touching. If you want to know if the fixtures themselves are really
        // touching you can use c->IsTouching() to check.
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
 
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 작성하고 빌드한다.

오브젝트 UserData 설정시 SetUserData()를 사용하면 안된다는 것에 주의한다. (아래 참고 참조)

 

실행하면 Player 오브젝트가 던져지고 벽과 바닥에 부딪힌다.

 

부딪힌 오브젝트와 몇 개의 포인트에서 충돌이 발생한지 확인 할 수 있다.

 

바닥에 완전히 정지하면 2개 포인트에서 충돌이 계속 발생한다.

 

※ 참고

Box2D Collision Module

Box2D Dynamic Module - Contacts

Box2D UserData

Box2D SetUserData()

 

반응형
Posted by J-sean
:
반응형

SDL과 2D Rigid Body Simulation Library Box2D를 사용해 보자.

 

아래 링크에서 Box2D를 다운로드 받고 빌드한다. (CMake를 설치하고 build.bat를 실행하면 된다)

빌드가 완료되면 비주얼 스튜디오 프로젝트를 만들고 Include, Library 디렉토리를 적당히 설정한다.

Box2D

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
 
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
 
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
 
    playerBody->CreateFixture(&fixtureDef);
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
 
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
 
    SDL_Rect groundRect = { 040050010 };
 
    groundBody->CreateFixture(&groundBox, 0.0f);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
 
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
 
    SDL_Rect wallRect = { 300010480 };
 
    wallBody->CreateFixture(&wallBox, 0.0f);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
 
        //printf("%4.2f %4.2f %4.2f\n", playerPosition.x, playerPosition.y, angle);
 
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

GIF 캡쳐 과정에서 화질이 많이 떨어졌지만 강체들의 물리 반응을 확인할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(30.0f, 0.0f);
    playerBodyDef.angularVelocity = 1.0f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
 
    //b2PolygonShape dynamicBox;
    //dynamicBox.SetAsBox((float)playerSurface->w / 2, (float)playerSurface->h / 2);
    b2CircleShape dynamicBox;
    dynamicBox.m_radius = (float)playerSurface->/ 2;
 
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
 
    playerBody->CreateFixture(&fixtureDef);
 

 

Player 캐릭터의 충돌을 감지하는 Shape을 원으로 바꿔보자.

 

스프라이트는 사각형이지만 Circle Shape의 움직임을 보여준다.

 

※ 참고

Box2D Documentation

 

반응형
Posted by J-sean
:

[SDL] SDL OpenCV

C, C++ 2024. 1. 27. 01:00 |
반응형

SDL에서 OpenCV를 사용해 보자.

 

아래와 같이 비주얼 스튜디오에서 SDL과 OpenCV를 사용할 수 있도록 적절히 세팅한다.

 

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include "SDL.h"
#include "opencv2/opencv.hpp"
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
 
    //////////////////// cv::Mat to SDL_Texture ////////////////////
    cv::Mat image = cv::imread("image.png", cv::IMREAD_COLOR);
    SDL_Rect destRect = { 00, image.cols, image.rows };
    SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR24,
        SDL_TEXTUREACCESS_STREAMING, image.cols, image.rows);
    SDL_UpdateTexture(texture, NULL, (void*)image.data, image.step1());
    // SDL_UpdateTexture() 대신 아래 주석 처리된 코드를 사용할 수도 있다.
    // #include <opencv2/core/types_c.h> 필요.
 
    /*
    IplImage iplImage = cvIplImage(image);
    unsigned char* texture_data = NULL;
    int texture_pitch = 0;
    SDL_LockTexture(texture, 0, (void**)&texture_data, &texture_pitch);
    memcpy(texture_data, (void*)iplImage.imageData,
        iplImage.width * iplImage.height * iplImage.nChannels);
    SDL_UnlockTexture(texture);
    */
    ////////////////////////////////////////////////////////////////
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
            default:
                break;
            }
        }
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL&destRect);
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 작성하고 빌드한다.

 

실행하면 이미지가 출력된다.

 

반응형
Posted by J-sean
:
반응형

SDL에서 ImGui를 이용해 간단한 그래픽 유저 인터페이스를 구현해 보자.

 

비주얼 스튜디오에서 C++ 프로젝트를 만들고 소스파일(Source.cpp)을 추가한다.

SDL을 사용하기 위한 Include, Library 디렉토리 설정 등을 진행하고 ..\imgui-1.90.1\backends 에서 imgui_impl_sdl2.*, imgui_impl_sdlrenderer2.* 파일들을 복사한다. 나머지 파일들은 ..\imgui-1.90.1 에서 복사한다.

 

필터(ImGui)를 추가하고 복사한 파일들을 프로젝트에 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <iostream>
#include "SDL.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
 
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
    //io.Fonts->AddFontFromFileTTF("newfont.ttf", 20);
    // If no fonts are loaded, dear imgui will use the default font.
 
    // Setup Dear ImGui style    
    ImGui::StyleColorsLight();
    //ImGui::StyleColorsDark();
 
    // Setup Platform/Renderer backends
    ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
    ImGui_ImplSDLRenderer2_Init(renderer);
 
    // Our state
    bool show_demo_window = true;
    bool show_another_window = false;
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
 
    SDL_Event event;
    bool quit = false;
 
    // Poll and handle events (inputs, window resize, etc.)
    // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to
    // use your inputs.
    // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or
    // clear/overwrite your copy of the mouse data.
    // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application,
    // or clear/overwrite your copy of the keyboard data.
    // Generally you may always pass all inputs to dear imgui, and hide them from your application based on
    // those two flags.
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            ImGui_ImplSDL2_ProcessEvent(&event);
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
            default:
                break;
            }
        }
 
        // Start the Dear ImGui frame
        ImGui_ImplSDLRenderer2_NewFrame();
        ImGui_ImplSDL2_NewFrame();
        ImGui::NewFrame();
 
        // Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
        {
            static float f = 0.0f;
            static int counter = 0;
 
            ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
 
            ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
            ImGui::Checkbox("Demo Window"&show_demo_window); // Edit bools storing our window open/close state
            ImGui::Checkbox("Another Window"&show_another_window);
 
            ImGui::SliderFloat("float"&f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
            ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
 
            if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
                counter++;
            ImGui::SameLine();
            ImGui::Text("counter = %d", counter);
 
            ImGui::Text("Application average %.3f ms/frame (%.1f FPS)"1000.0f / io.Framerate, io.Framerate);
            ImGui::End();
        }
 
        // Show another simple window.
        if (show_another_window)
        {
            ImGui::Begin("Another Window"&show_another_window);
            // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
            ImGui::Text("Hello from another window!");
            if (ImGui::Button("Close Me"))
                show_another_window = false;
            ImGui::End();
        }
 
        // Show only a button at (0, 0).
        {
            ImGui::SetNextWindowPos(ImVec2(00), ImGuiCond_Always);
            ImGui::Begin("No Name"NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration
                | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings);
            if (ImGui::Button("MyButton", ImVec2(200100))) {
                std::cout << "Clicked" << std::endl;
            }
            ImGui::End();
        }
 
        // Rendering
        ImGui::Render();
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
        ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
        SDL_RenderPresent(renderer);
    }
 
    // Cleanup
    ImGui_ImplSDLRenderer2_Shutdown();
    ImGui_ImplSDL2_Shutdown();
    ImGui::DestroyContext();
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

실행하면 윈도우에 GUI가 출력된다.

 

ImGui의 스타일(테마)은 아래와 같이 개별적으로도 지정할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Setup Dear ImGui style
//ImGui::StyleColorsLight();
//ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
 
style.Colors[ImGuiCol_Text] = ImVec4(0.86f, 0.93f, 0.89f, 0.78f);
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.86f, 0.93f, 0.89f, 0.28f);
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.14f, 0.17f, 1.00f);
style.Colors[ImGuiCol_Border] = ImVec4(0.31f, 0.31f, 1.00f, 0.00f);
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.22f, 0.27f, 1.00f);
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.92f, 0.18f, 0.29f, 0.78f);
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.20f, 0.22f, 0.27f, 1.00f);
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.20f, 0.22f, 0.27f, 0.75f);
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.20f, 0.22f, 0.27f, 0.47f);
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.22f, 0.27f, 1.00f);
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.09f, 0.15f, 0.16f, 1.00f);
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.92f, 0.18f, 0.29f, 0.78f);
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.71f, 0.22f, 0.27f, 1.00f);
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.47f, 0.77f, 0.83f, 0.14f);
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_Button] = ImVec4(0.92f, 0.18f, 0.29f, 0.70f);
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.92f, 0.18f, 0.29f, 0.86f);
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_Header] = ImVec4(0.92f, 0.18f, 0.29f, 0.76f);
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.92f, 0.18f, 0.29f, 0.86f);
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_Separator] = ImVec4(0.14f, 0.16f, 0.19f, 1.00f);
style.Colors[ImGuiCol_SeparatorHovered] = ImVec4(0.92f, 0.18f, 0.29f, 0.78f);
style.Colors[ImGuiCol_SeparatorActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.47f, 0.77f, 0.83f, 0.04f);
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.92f, 0.18f, 0.29f, 0.78f);
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_PlotLines] = ImVec4(0.86f, 0.93f, 0.89f, 0.63f);
style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.86f, 0.93f, 0.89f, 0.63f);
style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(0.92f, 0.18f, 0.29f, 1.00f);
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.92f, 0.18f, 0.29f, 0.43f);
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.20f, 0.22f, 0.27f, 0.9f);
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.22f, 0.27f, 0.73f);
 

 

 

※ 참고

Dear ImGui는 API Reference가 없다. Python 래퍼 pyimgui 를 참고하자.

pyimgui

 

반응형
Posted by J-sean
: