Merge pull request #7 from mephi1984/Albert

Albert
This commit is contained in:
Albrut 2025-03-01 21:19:28 +06:00 committed by GitHub
commit 60380ee733
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 638 additions and 63 deletions

17
ActiveObject.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "TextureManager.h"
#include "Math.h"
#include <memory>
struct ActiveObject {
std::shared_ptr<ZL::Texture> activeObjectTexturePtr;
ZL::VertexDataStruct activeObjectMesh;
ZL::VertexRenderStruct activeObjectMeshMutable;
std::shared_ptr<ZL::Texture> activeObjectScreenTexturePtr;
ZL::VertexDataStruct activeObjectScreenMesh;
ZL::VertexRenderStruct activeObjectScreenMeshMutable;
ZL::Vector3f objectPos;
bool highlighted = false;
};

18
Environment.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "Environment.h"
namespace ZL {
int Environment::windowHeaderHeight = 0;
int Environment::width = 0;
int Environment::height = 0;
float Environment::zoom = 10.0f;
bool Environment::leftPressed = false;
bool Environment::rightPressed = false;
bool Environment::upPressed = false;
bool Environment::downPressed = false;
Vector3f Environment::cameraShift = {0, 0, 0};
Vector3f Environment::characterPos = {0, 0, 0};
} // namespace ZL

22
Environment.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "Math.h"
namespace ZL {
class Environment {
public:
static int windowHeaderHeight;
static int width;
static int height;
static float zoom;
static bool leftPressed;
static bool rightPressed;
static bool upPressed;
static bool downPressed;
static Vector3f cameraShift;
static Vector3f characterPos;
};
} // namespace ZL

200
Game.cpp
View File

@ -1,8 +1,206 @@
#include "Game.h"
#include "AnimatedModel.h"
#include "BoneAnimatedModel.h"
#include "Utils.h"
#include "Inventory.h" // Add this include
#include <SDL2/SDL.h>
#include <iostream>
namespace ZL
{
}
void Game::worldToScreenCoordinates(Vector3f objectPos,
Matrix4f projectionModelView,
int screenWidth, int screenHeight,
int& screenX, int& screenY) {
Vector4f inx = { objectPos.v[0], objectPos.v[1], objectPos.v[2], 1.0f};
Vector4f clipCoords = MultMatrixVector(projectionModelView, inx);
float ndcX = clipCoords.v[0] / clipCoords.v[3];
float ndcY = clipCoords.v[1] / clipCoords.v[3];
screenX = (int)((ndcX + 1.0f) * 0.5f * screenWidth);
screenY = (int)((1.0f + ndcY) * 0.5f * screenHeight);
}
Game::Game() : window(nullptr), glContext(nullptr), exitGameLoop(false),
newTickCount(0), lastTickCount(0) {
}
Game::~Game() {
if (glContext) {
SDL_GL_DeleteContext(glContext);
}
if (window) {
SDL_DestroyWindow(window);
}
SDL_Quit();
}
void Game::setup() {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
return;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
window = SDL_CreateWindow("Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
Environment::width, Environment::height, SDL_WINDOW_OPENGL);
glContext = SDL_GL_CreateContext(window);
ZL::BindOpenGlFunctions();
ZL::CheckGlError();
// Initialize renderer
renderer.shaderManager.AddShaderFromFiles("default", "./default.vertex", "./default.fragment");
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./defaultColor.vertex", "./defaultColor.fragment");
renderer.InitOpenGL();
// Initialize game objects
gameObjects.initialize();
}
void Game::drawScene() {
static const std::string defaultShaderName = "default";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, Environment::width, Environment::height);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
// 3D Scene rendering
{
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
50, 10000);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -100 * Environment::zoom });
float t = 0.3;
renderer.RotateMatrix(QuatFromRotateAroundX(t * M_PI / 2.0));
// Draw cone
glBindTexture(GL_TEXTURE_2D, gameObjects.coneTexturePtr->getTexID());
renderer.DrawVertexRenderStruct(gameObjects.coneMeshMutable);
renderer.TranslateMatrix(Environment::cameraShift);
// Draw active objects
for (auto& ao : gameObjects.activeObjects) {
renderer.PushMatrix();
renderer.TranslateMatrix(ao.objectPos);
glBindTexture(GL_TEXTURE_2D, ao.activeObjectTexturePtr->getTexID());
renderer.DrawVertexRenderStruct(ao.activeObjectMeshMutable);
renderer.PopMatrix();
}
// Draw room
glBindTexture(GL_TEXTURE_2D, gameObjects.roomTexturePtr->getTexID());
renderer.DrawVertexRenderStruct(gameObjects.textMeshMutable);
auto latestProjectionModelView = renderer.GetProjectionModelViewMatrix();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
// 2D UI rendering
renderer.PushProjectionMatrix(static_cast<float>(Environment::width),
static_cast<float>(Environment::height));
renderer.PushMatrix();
renderer.LoadIdentity();
// Draw highlighted objects UI
for (auto& ao : gameObjects.activeObjects) {
if (ao.highlighted) {
int screenX, screenY;
worldToScreenCoordinates(ao.objectPos, latestProjectionModelView,
Environment::width, Environment::height, screenX, screenY);
renderer.PushMatrix();
renderer.TranslateMatrix(Vector3f{screenX + 0.f, screenY + 0.f, 0.0f});
glBindTexture(GL_TEXTURE_2D, ao.activeObjectScreenTexturePtr->getTexID());
renderer.DrawVertexRenderStruct(ao.activeObjectScreenMeshMutable);
renderer.PopMatrix();
}
}
// Draw inventory
const auto& inventory = ZL::ReturnInventory();
for (size_t i = 0; i < inventory.size(); ++i) {
renderer.PushMatrix();
float xPos = Environment::width - gameObjects.INVENTORY_MARGIN - gameObjects.INVENTORY_ICON_SIZE;
float yPos = gameObjects.INVENTORY_MARGIN + i * (gameObjects.INVENTORY_ICON_SIZE + gameObjects.INVENTORY_MARGIN);
renderer.TranslateMatrix(Vector3f{xPos, yPos, 0.0f});
glBindTexture(GL_TEXTURE_2D, inventory[i].texture->getTexID());
renderer.DrawVertexRenderStruct(gameObjects.inventoryIconMeshMutable);
renderer.PopMatrix();
}
renderer.PopMatrix();
renderer.PopProjectionMatrix();
}
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::processTickCount() {
if (lastTickCount == 0) {
lastTickCount = SDL_GetTicks64();
return;
}
newTickCount = SDL_GetTicks64();
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ?
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
gameObjects.update();
lastTickCount = newTickCount;
}
}
void Game::render() {
SDL_GL_MakeCurrent(window, glContext);
ZL::CheckGlError();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawScene();
processTickCount();
SDL_GL_SwapWindow(window);
}
void Game::update() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
exitGameLoop = true;
}
gameObjects.handleEvent(event);
}
render();
}
} // namespace ZL

47
Game.h
View File

@ -1,14 +1,43 @@
#pragma once
#include "Math.h"
#include "Physics.h"
#include "TextureManager.h"
#include <SDL2/SDL.h>
#include "GameObjectManager.h"
#include "Renderer.h"
#include "AnimatedModel.h"
#include "BoneAnimatedModel.h"
#include <memory>
#include "Environment.h"
namespace ZL
{
namespace ZL {
}
class Game {
public:
Game();
~Game();
void setup();
void run();
void update();
void render();
bool shouldExit() const { return exitGameLoop; }
private:
void processTickCount();
void drawScene();
void worldToScreenCoordinates(Vector3f objectPos,
Matrix4f projectionModelView,
int screenWidth, int screenHeight,
int& screenX, int& screenY);
SDL_Window* window;
SDL_GLContext glContext;
Renderer renderer;
GameObjectManager gameObjects;
bool exitGameLoop;
size_t newTickCount;
size_t lastTickCount;
static const size_t CONST_TIMER_INTERVAL = 10;
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
};
} // namespace ZL

181
GameObjectManager.cpp Normal file
View File

@ -0,0 +1,181 @@
#include "GameObjectManager.h"
#include "Environment.h"
#include "ObjLoader.h"
#include "Inventory.h"
#include "TextModel.h" // Add this include for LoadFromTextFile
namespace ZL {
const float GameObjectManager::INVENTORY_ICON_SIZE = 32.0f;
const float GameObjectManager::INVENTORY_MARGIN = 10.0f;
void GameObjectManager::initialize() {
// Load textures
roomTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./Kitchen_ceramics.bmp"));
coneTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./conus.bmp"));
// Load models
colorCubeMesh = CreateCube3D(5.0);
colorCubeMeshMutable.data = CreateCube3D(5.0);
colorCubeMeshMutable.RefreshVBO();
testObjMesh = LoadFromObjFile("./chair_01.obj");
testObjMesh.Scale(10);
testObjMesh.SwapZandY();
testObjMeshMutable.data = testObjMesh;
testObjMeshMutable.RefreshVBO();
textMesh = ZL::LoadFromTextFile("./mesh001.txt"); // Add ZL:: namespace
coneMesh = ZL::LoadFromTextFile("./cone001.txt"); // Add ZL:: namespace
coneMesh.Scale(200);
textMeshMutable.AssignFrom(textMesh);
textMeshMutable.RefreshVBO();
coneMeshMutable.AssignFrom(coneMesh);
coneMeshMutable.RefreshVBO();
// Load bone animations
bx.LoadFromFile("mesh_armature_and_animation_data.txt");
// Create active object
ActiveObject ao1;
ao1.activeObjectMesh = ZL::LoadFromTextFile("./book001.txt"); // Add ZL:: namespace
ao1.activeObjectMesh.Scale(4);
ao1.activeObjectMeshMutable.AssignFrom(ao1.activeObjectMesh);
ao1.activeObjectMeshMutable.RefreshVBO();
ao1.objectPos = Vector3f{50, 0, -300};
ao1.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./book03.bmp"));
ao1.activeObjectScreenTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./aoscreen01.bmp"));
ao1.activeObjectScreenMesh = CreateRect2D({ 0.f, 0.f }, { 64.f, 64.f }, 0.5);
ao1.activeObjectScreenMeshMutable.AssignFrom(ao1.activeObjectScreenMesh);
ao1.activeObjectScreenMeshMutable.RefreshVBO();
activeObjects.push_back(ao1);
// Initialize audio
audioPlayer = std::make_unique<AudioPlayer>();
if (audioPlayer) {
audioPlayer->playMusic("Symphony No.6 (1st movement).ogg");
}
// Initialize inventory
inventoryIconMesh = CreateRect2D(
{0.0f, 0.0f},
{INVENTORY_ICON_SIZE/2, INVENTORY_ICON_SIZE/2},
0.5f
);
inventoryIconMeshMutable.AssignFrom(inventoryIconMesh);
inventoryIconMeshMutable.RefreshVBO();
// Add test items to inventory
auto testRoomTexture = std::make_shared<Texture>(CreateTextureDataFromBmp24("./Kitchen_ceramics.bmp"));
auto testConeTexture = std::make_shared<Texture>(CreateTextureDataFromBmp24("./conus.bmp"));
AddItemToInventory("RoomCeramics", testRoomTexture);
AddItemToInventory("Cone", testConeTexture);
}
void GameObjectManager::update() {
updateScene(16); // Добавим фиксированный timestep для обновления сцены
}
void GameObjectManager::handleEvent(const SDL_Event& event) {
if (event.type == SDL_MOUSEBUTTONDOWN) {
bx.Interpolate(animationCounter);
animationCounter += 2;
}
else if (event.type == SDL_MOUSEWHEEL) {
static const float zoomstep = 1.0f;
if (event.wheel.y > 0) {
Environment::zoom -= zoomstep;
}
else if (event.wheel.y < 0) {
Environment::zoom += zoomstep;
}
if (Environment::zoom < zoomstep) {
Environment::zoom = zoomstep;
}
}
else if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
case SDLK_a:
Environment::leftPressed = true;
if (audioPlayer) {
audioPlayer->playSound("Звук-Идут-по-земле.ogg");
}
break;
case SDLK_RIGHT:
case SDLK_d:
Environment::rightPressed = true;
if (audioPlayer) {
audioPlayer->playSound("Звук-Идут-по-земле.ogg");
}
break;
case SDLK_UP:
case SDLK_w:
Environment::upPressed = true;
if (audioPlayer) {
audioPlayer->playSound("Звук-Идут-по-земле.ogg");
}
break;
case SDLK_DOWN:
case SDLK_s:
Environment::downPressed = true;
if (audioPlayer) {
audioPlayer->playSound("Звук-Идут-по-земле.ogg");
}
break;
// ...handle other keys...
}
}
else if (event.type == SDL_KEYUP) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
case SDLK_a:
Environment::leftPressed = false;
break;
case SDLK_RIGHT:
case SDLK_d:
Environment::rightPressed = false;
break;
case SDLK_UP:
case SDLK_w:
Environment::upPressed = false;
break;
case SDLK_DOWN:
case SDLK_s:
Environment::downPressed = false;
break;
}
}
}
void GameObjectManager::updateScene(size_t ms) {
const float SPEED = 0.1f;
if (Environment::leftPressed) {
Environment::cameraShift.v[0] += SPEED * ms;
}
if (Environment::rightPressed) {
Environment::cameraShift.v[0] -= SPEED * ms;
}
if (Environment::upPressed) {
Environment::cameraShift.v[2] += SPEED * ms;
}
if (Environment::downPressed) {
Environment::cameraShift.v[2] -= SPEED * ms;
}
Environment::characterPos.v[0] = -Environment::cameraShift.v[0];
Environment::characterPos.v[1] = -Environment::cameraShift.v[1];
Environment::characterPos.v[2] = -Environment::cameraShift.v[2];
for (auto& ao : activeObjects) {
float dist = sqrtf(
pow(Environment::characterPos.v[0] - ao.objectPos.v[0], 2) +
pow(Environment::characterPos.v[1] - ao.objectPos.v[1], 2) +
pow(Environment::characterPos.v[2] - ao.objectPos.v[2], 2)
);
ao.highlighted = (dist < 50.f);
}
}
} // namespace ZL

51
GameObjectManager.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include "TextureManager.h"
#include "BoneAnimatedModel.h"
#include "cmakeaudioplayer/include/AudioPlayer.hpp"
#include <memory>
#include <vector>
#include "ActiveObject.h"
#include <SDL2/SDL.h>
namespace ZL {
class GameObjectManager {
public:
void initialize();
void update();
void handleEvent(const SDL_Event& event);
void updateScene(size_t ms);
std::shared_ptr<ZL::Texture> testObjTexturePtr;
std::shared_ptr<ZL::Texture> roomTexturePtr;
std::shared_ptr<ZL::Texture> coneTexturePtr;
ZL::VertexDataStruct colorCubeMesh;
ZL::VertexRenderStruct colorCubeMeshMutable;
ZL::VertexDataStruct testObjMesh;
ZL::VertexRenderStruct testObjMeshMutable;
ZL::BoneSystem bx;
ZL::VertexRenderStruct bxMutable;
ZL::VertexDataStruct textMesh;
ZL::VertexRenderStruct textMeshMutable;
ZL::VertexDataStruct coneMesh;
ZL::VertexRenderStruct coneMeshMutable;
std::vector<ActiveObject> activeObjects;
std::unique_ptr<AudioPlayer> audioPlayer;
ZL::VertexDataStruct inventoryIconMesh;
ZL::VertexRenderStruct inventoryIconMeshMutable;
static const float INVENTORY_ICON_SIZE;
static const float INVENTORY_MARGIN;
private:
int animationCounter = 0;
};
} // namespace ZL

23
GameWorld.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "GameWorld.h"
#include "InputManager.h"
namespace ZL {
GameWorld& GameWorld::getInstance() {
static GameWorld instance;
return instance;
}
void GameWorld::update(float deltaTime) {
auto& input = InputManager::getInstance();
// Update game state based on input
if (input.isKeyPressed(SDL_SCANCODE_W)) {
// Move forward
}
// ... handle other game logic ...
}
void GameWorld::addObject(const Vector3f& position) {
mGameObjects.push_back(position);
}
}

18
GameWorld.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "Vector3f.h"
#include <vector>
#include <memory>
namespace ZL {
class GameWorld {
public:
static GameWorld& getInstance();
void update(float deltaTime);
void addObject(const Vector3f& position);
// ...other game world related methods...
private:
GameWorld() = default;
std::vector<Vector3f> mGameObjects;
};
}

27
InputManager.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "InputManager.h"
namespace ZL {
InputManager& InputManager::getInstance() {
static InputManager instance;
return instance;
}
void InputManager::processInput() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
mShouldQuit = true;
}
else if (event.type == SDL_KEYDOWN) {
mKeys[event.key.keysym.scancode] = true;
}
else if (event.type == SDL_KEYUP) {
mKeys[event.key.keysym.scancode] = false;
}
}
}
bool InputManager::isKeyPressed(SDL_Scancode key) const {
return mKeys[key];
}
}

17
InputManager.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <SDL2/SDL.h>
namespace ZL {
class InputManager {
public:
static InputManager& getInstance();
void processInput();
bool isKeyPressed(SDL_Scancode key) const;
bool shouldQuit() const { return mShouldQuit; }
private:
InputManager() = default;
bool mKeys[SDL_NUM_SCANCODES] = {false};
bool mShouldQuit = false;
};
}

View File

@ -411,15 +411,8 @@ namespace ZL
ZL::AddItemToInventory("RoomCeramics", roomTexturePtr);
ZL::AddItemToInventory("Cone", coneTexturePtr);
// std::cout << "Before removal:\n";
ZL::PrintInventory();
// Удаляем "Cone" из инвентаря
// ZL::RemoveItemFromInventory("Cone");
// std::cout << "\nAfter removal:\n";
// ZL::PrintInventory();
// Initialize audio player and start background music
GameObjects::audioPlayer = std::make_unique<AudioPlayer>();
if (GameObjects::audioPlayer) {
@ -547,57 +540,27 @@ namespace ZL
};
int main(int argc, char* argv[])
{
#include "Game.h"
#include "Environment.h"
constexpr int CONST_WIDTH = 1280;
constexpr int CONST_HEIGHT = 720;
int main(int argc, char* argv[]) {
constexpr int CONST_WIDTH = 1280;
constexpr int CONST_HEIGHT = 720;
ZL::Env::width = CONST_WIDTH;
ZL::Env::height = CONST_HEIGHT;
ZL::Environment::width = CONST_WIDTH;
ZL::Environment::height = CONST_HEIGHT;
ZL::Game game;
game.setup();
#ifdef EMSCRIPTEN
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Renderer* renderer = NULL;
SDL_CreateWindowAndRenderer(CONST_WIDTH, CONST_HEIGHT, SDL_WINDOW_OPENGL, &ZL::window, &renderer);
emscripten_set_main_loop([](){ game.update(); }, 0, 1);
#else
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
return 1;
}
// Use a core profile setup.
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
ZL::window = SDL_CreateWindow("Jumping Bird", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CONST_WIDTH, CONST_HEIGHT, SDL_WINDOW_OPENGL);
#endif
//todo
ZL::Env::windowHeaderHeight = 0;
ZL::gl_context = SDL_GL_CreateContext(ZL::window);
ZL::CheckGlError();
ZL::setup();
#ifdef EMSCRIPTEN
// register update as callback
emscripten_set_main_loop(ZL::update, 0, 1);
#else
while (!ZL::ExitGameLoop) {
ZL::update();
SDL_Delay(2);
}
SDL_GL_DeleteContext(ZL::gl_context);
SDL_DestroyWindow(ZL::window);
SDL_Quit();
exit(0);
while (!game.shouldExit()) {
game.update();
SDL_Delay(2);
}
#endif
return 0;
}

11
start.sh Executable file
View File

@ -0,0 +1,11 @@
g++ Game.cpp main.cpp Math.cpp OpenGlExtensions.cpp Physics.cpp Renderer.cpp \
ShaderManager.cpp TextureManager.cpp Utils.cpp BoneAnimatedModel.cpp \
ObjLoader.cpp cmakeaudioplayer/src/AudioPlayer.cpp TextModel.cpp \
Inventory.cpp Environment.cpp GameObjectManager.cpp \
-o sdl_app -O2 -std=c++17 \
-I cmakeaudioplayer/include \
$(pkg-config --cflags --libs sdl2 gl) \
$(pkg-config --cflags --libs vorbis vorbisfile ogg) \
-lopenal
./sdl_app