mirror of
https://github.com/celisej567/mcpe.git
synced 2026-09-23 20:09:46 +03:00
SDL/WASM Port
This commit is contained in:
committed by
iProgramInCpp
parent
0b16b2843a
commit
0fbe90752d
1
platforms/.gitignore
vendored
Normal file
1
platforms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build*
|
||||
@@ -15,6 +15,12 @@
|
||||
|
||||
// Add sound system overrides here
|
||||
|
||||
#elif defined(USE_SDL)
|
||||
|
||||
// -- OpenAL based sound system for SDL
|
||||
#include "sdl/SoundSystemAL.hpp"
|
||||
#define SOUND_SYSTEM_TYPE SoundSystemAL
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
// -- DirectSound based sound system for Windows
|
||||
|
||||
2
platforms/sdl/.gitignore
vendored
Normal file
2
platforms/sdl/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/build
|
||||
/assets
|
||||
308
platforms/sdl/AppPlatform_sdl.cpp
Normal file
308
platforms/sdl/AppPlatform_sdl.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
#include "AppPlatform_sdl.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include <png.h>
|
||||
|
||||
#include "compat/GL.hpp"
|
||||
#else
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#include "client/common/Utils.hpp"
|
||||
|
||||
AppPlatform_sdl::AppPlatform_sdl(std::string storageDir, SDL_Window *window) {
|
||||
_storageDir = storageDir;
|
||||
_window = window;
|
||||
}
|
||||
|
||||
int AppPlatform_sdl::checkLicense() {
|
||||
// we own the game!!
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Ensure Screenshots Folder Exists
|
||||
void ensure_screenshots_folder(const char *screenshots) {
|
||||
// Check Screenshots Folder
|
||||
struct stat obj;
|
||||
if (stat(screenshots, &obj) != 0 || !S_ISDIR(obj.st_mode)) {
|
||||
// Create Screenshots Folder
|
||||
#ifdef _WIN32
|
||||
int ret = mkdir(screenshots);
|
||||
#else
|
||||
int ret = mkdir(screenshots, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
// Unable To Create Folder
|
||||
LogMsg("Error Creating Directory: %s: %s", screenshots, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
// 4 (Year) + 1 (Hyphen) + 2 (Month) + 1 (Hyphen) + 2 (Day) + 1 (Underscore) + 2 (Hour) + 1 (Period) + 2 (Minute) + 1 (Period) + 2 (Second) + 1 (Null Terminator)
|
||||
#define TIME_SIZE 20
|
||||
// Take Screenshot
|
||||
static int save_png(const char *filename, unsigned char *pixels, int line_size, int width, int height) {
|
||||
// Return value
|
||||
int ret = 0;
|
||||
|
||||
// Variables
|
||||
png_structp png = NULL;
|
||||
png_infop info = NULL;
|
||||
FILE *file = NULL;
|
||||
png_colorp palette = NULL;
|
||||
png_bytep rows[height];
|
||||
for (int i = 0; i < height; ++i) {
|
||||
rows[height - i - 1] = (png_bytep)(&pixels[i * line_size]);
|
||||
}
|
||||
|
||||
// Init
|
||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png) {
|
||||
ret = 1;
|
||||
goto ret;
|
||||
}
|
||||
info = png_create_info_struct(png);
|
||||
if (!info) {
|
||||
ret = 1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
// Open File
|
||||
file = fopen(filename, "wb");
|
||||
if (!file) {
|
||||
ret = 1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
// Prepare To Write
|
||||
png_init_io(png, file);
|
||||
png_set_IHDR(png, info, width, height, 8 /* Depth */, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
palette = (png_colorp) png_malloc(png, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
|
||||
if (!palette) {
|
||||
ret = 1;
|
||||
goto ret;
|
||||
}
|
||||
png_set_PLTE(png, info, palette, PNG_MAX_PALETTE_LENGTH);
|
||||
png_write_info(png, info);
|
||||
png_set_packing(png);
|
||||
|
||||
// Write
|
||||
png_write_image(png, rows);
|
||||
png_write_end(png, info);
|
||||
|
||||
ret:
|
||||
// Free
|
||||
if (palette != NULL) {
|
||||
png_free(png, palette);
|
||||
}
|
||||
if (file != NULL) {
|
||||
fclose(file);
|
||||
}
|
||||
if (png != NULL) {
|
||||
png_destroy_write_struct(&png, &info);
|
||||
}
|
||||
|
||||
// Return
|
||||
return ret;
|
||||
}
|
||||
void AppPlatform_sdl::saveScreenshot(const std::string &filename, int glWidth, int glHeight) {
|
||||
// Get Directory
|
||||
std::string screenshots = _storageDir + "/screenshots";
|
||||
|
||||
// Get Timestamp
|
||||
time_t rawtime;
|
||||
struct tm *timeinfo;
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
char time[TIME_SIZE];
|
||||
strftime(time, TIME_SIZE, "%Y-%m-%d_%H.%M.%S", timeinfo);
|
||||
|
||||
// Ensure Screenshots Folder Exists
|
||||
ensure_screenshots_folder(screenshots.c_str());
|
||||
|
||||
// Prevent Overwriting Screenshots
|
||||
int num = 1;
|
||||
std::string file = screenshots + '/' + time + ".png";
|
||||
while (access(file.c_str(), F_OK) != -1) {
|
||||
file = screenshots + '/' + time + '-' + std::to_string(num) + ".png";
|
||||
num++;
|
||||
}
|
||||
|
||||
// Get Image Size
|
||||
GLint viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
int x = viewport[0];
|
||||
int y = viewport[1];
|
||||
int width = viewport[2];
|
||||
int height = viewport[3];
|
||||
|
||||
// Get Line Size
|
||||
int line_size = width * 4;
|
||||
{
|
||||
// Handle Alignment
|
||||
int alignment;
|
||||
glGetIntegerv(GL_PACK_ALIGNMENT, &alignment);
|
||||
// Round
|
||||
int diff = line_size % alignment;
|
||||
if (diff > 0) {
|
||||
line_size = line_size + (alignment - diff);
|
||||
}
|
||||
}
|
||||
int size = height * line_size;
|
||||
|
||||
// Read Pixels
|
||||
bool fail = false;
|
||||
unsigned char *pixels = (unsigned char *) malloc(size);
|
||||
if (pixels == NULL) {
|
||||
fail = true;
|
||||
} else {
|
||||
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
}
|
||||
|
||||
// Save Image
|
||||
if (fail || save_png(file.c_str(), pixels, line_size, width, height)) {
|
||||
LogMsg("Screenshot Failed: %s", file.c_str());
|
||||
} else {
|
||||
LogMsg("Screenshot Saved: %s", file.c_str());
|
||||
}
|
||||
|
||||
// Free
|
||||
if (pixels != NULL) {
|
||||
free(pixels);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int AppPlatform_sdl::getScreenWidth() const {
|
||||
int width;
|
||||
SDL_GL_GetDrawableSize(_window, &width, nullptr);
|
||||
return width;
|
||||
}
|
||||
|
||||
int AppPlatform_sdl::getScreenHeight() const {
|
||||
int height;
|
||||
SDL_GL_GetDrawableSize(_window, nullptr, &height);
|
||||
return height;
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static void png_read_sdl(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
SDL_RWread((SDL_RWops *) png_get_io_ptr(png_ptr), (char *) data, length, 1);
|
||||
}
|
||||
static void nop_png_warning(png_structp png_ptr, png_const_charp warning_message) {
|
||||
// Do Nothing
|
||||
}
|
||||
#endif
|
||||
Texture AppPlatform_sdl::loadTexture(const std::string& str, bool b) {
|
||||
Texture out;
|
||||
out.field_C = 0;
|
||||
out.field_D = 0;
|
||||
|
||||
std::string realPath = str;
|
||||
if (realPath.size() && realPath[0] == '/') {
|
||||
// trim it off
|
||||
realPath = realPath.substr(1);
|
||||
}
|
||||
realPath = "assets/" + realPath;
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_RWops *io = SDL_RWFromFile(realPath.c_str(), "rb");
|
||||
|
||||
if (io != NULL) {
|
||||
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, nop_png_warning);
|
||||
if (!pngPtr) {
|
||||
return out;
|
||||
}
|
||||
|
||||
png_infop infoPtr = png_create_info_struct(pngPtr);
|
||||
if (!infoPtr) {
|
||||
png_destroy_read_struct(&pngPtr, NULL, NULL);
|
||||
return out;
|
||||
}
|
||||
|
||||
png_set_read_fn(pngPtr, (png_voidp) io, png_read_sdl);
|
||||
|
||||
png_read_info(pngPtr, infoPtr);
|
||||
|
||||
png_set_expand(pngPtr);
|
||||
png_set_strip_16(pngPtr);
|
||||
png_set_gray_to_rgb(pngPtr);
|
||||
png_read_update_info(pngPtr, infoPtr);
|
||||
|
||||
out.m_width = png_get_image_width(pngPtr, infoPtr);
|
||||
out.m_height = png_get_image_height(pngPtr, infoPtr);
|
||||
|
||||
bool opaque = png_get_color_type(pngPtr, infoPtr) != PNG_COLOR_TYPE_RGBA;
|
||||
if (!opaque) {
|
||||
out.field_C = 1;
|
||||
}
|
||||
int pixelSize = opaque ? 3 : 4;
|
||||
|
||||
png_bytep *rowPtrs = new png_bytep[out.m_height];
|
||||
unsigned char *pixels = new unsigned char[pixelSize * out.m_width * out.m_height];
|
||||
|
||||
int rowStrideBytes = pixelSize * out.m_width;
|
||||
for (int i = 0; i < out.m_height; i++) {
|
||||
rowPtrs[i] = (png_bytep) &pixels[i * rowStrideBytes];
|
||||
}
|
||||
png_read_image(pngPtr, rowPtrs);
|
||||
|
||||
out.m_pixels = (uint32_t *) pixels;
|
||||
|
||||
png_destroy_read_struct(&pngPtr, &infoPtr, (png_infopp) 0);
|
||||
delete[](png_bytep) rowPtrs;
|
||||
SDL_RWclose(io);
|
||||
}
|
||||
#else
|
||||
char *data = emscripten_get_preloaded_image_data(("/" + realPath).c_str(), &out.m_width, &out.m_height);
|
||||
if (data != NULL) {
|
||||
size_t data_size = out.m_width * out.m_height * 4;
|
||||
out.m_pixels = (uint32_t *) new unsigned char[data_size];
|
||||
out.field_C = 1;
|
||||
memcpy(out.m_pixels, data, data_size);
|
||||
free(data);
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
LogMsg("Couldn't find file: %s", str.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppPlatform_sdl::setMouseGrabbed(bool b) {
|
||||
SDL_SetWindowGrab(_window, b ? SDL_TRUE : SDL_FALSE);
|
||||
SDL_SetRelativeMouseMode(b ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
|
||||
void AppPlatform_sdl::setMouseDiff(int x, int y) {
|
||||
xrel = x;
|
||||
yrel = y;
|
||||
}
|
||||
|
||||
void AppPlatform_sdl::getMouseDiff(int& x, int& y) {
|
||||
x = xrel;
|
||||
y = yrel;
|
||||
}
|
||||
|
||||
void AppPlatform_sdl::clearDiff() {
|
||||
xrel = 0;
|
||||
yrel = 0;
|
||||
}
|
||||
|
||||
bool AppPlatform_sdl::shiftPressed() {
|
||||
return m_bShiftPressed[0] || m_bShiftPressed[1];
|
||||
}
|
||||
|
||||
void AppPlatform_sdl::setShiftPressed(bool b, bool isLeft) {
|
||||
m_bShiftPressed[isLeft ? 0 : 1] = b;
|
||||
}
|
||||
|
||||
int AppPlatform_sdl::getUserInputStatus() {
|
||||
return -1;
|
||||
}
|
||||
45
platforms/sdl/AppPlatform_sdl.hpp
Normal file
45
platforms/sdl/AppPlatform_sdl.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "AppPlatform.hpp"
|
||||
|
||||
#ifdef ORIGINAL_CODE
|
||||
#error "This isn't original code. You probably shouldn't try to compile this"
|
||||
#endif
|
||||
|
||||
void ensure_screenshots_folder(const char *screenshots);
|
||||
|
||||
class AppPlatform_sdl : public AppPlatform {
|
||||
public:
|
||||
AppPlatform_sdl(std::string storageDir, SDL_Window *window);
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
void saveScreenshot(const std::string& fileName, int width, int height) override;
|
||||
#endif
|
||||
int checkLicense() override;
|
||||
int getScreenWidth() const override;
|
||||
int getScreenHeight() const override;
|
||||
Texture loadTexture(const std::string& str, bool b) override;
|
||||
int getUserInputStatus() override;
|
||||
|
||||
// Also add these to allow proper turning within the game.
|
||||
void setMouseGrabbed(bool b) override;
|
||||
void setMouseDiff(int x, int y);
|
||||
void getMouseDiff(int& x, int& y) override;
|
||||
void clearDiff() override;
|
||||
|
||||
// Also add these to allow proper text input within the game.
|
||||
bool shiftPressed() override;
|
||||
void setShiftPressed(bool b, bool isLeft);
|
||||
private:
|
||||
std::string _storageDir;
|
||||
SDL_Window *_window;
|
||||
|
||||
bool m_bShiftPressed[2] = {false, false};
|
||||
|
||||
int xrel;
|
||||
int yrel;
|
||||
};
|
||||
64
platforms/sdl/CMakeLists.txt
Normal file
64
platforms/sdl/CMakeLists.txt
Normal file
@@ -0,0 +1,64 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
project(reminecraftpe)
|
||||
|
||||
# SDL Build
|
||||
add_compile_definitions(USE_SDL HANDLE_CHARS_SEPARATELY)
|
||||
|
||||
# WASM
|
||||
if(EMSCRIPTEN)
|
||||
function(add_compile_and_link_options)
|
||||
add_compile_options(${ARGV})
|
||||
add_link_options(${ARGV})
|
||||
endfunction()
|
||||
set(CMAKE_EXECUTABLE_SUFFIX ".js")
|
||||
endif()
|
||||
|
||||
# Clang
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_compile_options(-Wno-inconsistent-missing-override -Wno-enum-compare-switch -Wno-register)
|
||||
endif()
|
||||
|
||||
# Threads
|
||||
if(EMSCRIPTEN)
|
||||
add_compile_and_link_options(-pthread)
|
||||
else()
|
||||
find_package(Threads)
|
||||
link_libraries(Threads::Threads)
|
||||
endif()
|
||||
|
||||
# Build
|
||||
add_executable(reminecraftpe
|
||||
main.cpp
|
||||
AppPlatform_sdl.cpp
|
||||
SoundSystemAL.cpp
|
||||
)
|
||||
|
||||
# Core
|
||||
add_subdirectory(../../source source)
|
||||
target_link_libraries(reminecraftpe reminecraftpe-core)
|
||||
|
||||
# LibPNG
|
||||
if(NOT EMSCRIPTEN)
|
||||
find_package(PNG REQUIRED)
|
||||
target_link_libraries(reminecraftpe PNG::PNG)
|
||||
endif()
|
||||
|
||||
# SDL
|
||||
if(TARGET SDL2::SDL2main)
|
||||
target_link_libraries(reminecraftpe SDL2::SDL2main)
|
||||
endif()
|
||||
|
||||
# WASM
|
||||
if(EMSCRIPTEN)
|
||||
target_link_options(reminecraftpe PRIVATE -Wno-pthreads-mem-growth)
|
||||
target_link_options(reminecraftpe PRIVATE -sALLOW_MEMORY_GROWTH=1)
|
||||
# Export Resize Function
|
||||
target_link_options(reminecraftpe PRIVATE -sEXPORTED_FUNCTIONS=_main,_resize_from_js -sEXPORTED_RUNTIME_METHODS=ccall)
|
||||
endif()
|
||||
|
||||
# Assets
|
||||
if(EMSCRIPTEN)
|
||||
target_link_options(reminecraftpe PRIVATE --use-preload-plugins --preload-file "${CMAKE_CURRENT_SOURCE_DIR}/assets@/assets")
|
||||
elseif(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/assets")
|
||||
file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${CMAKE_CURRENT_BINARY_DIR}/assets" SYMBOLIC)
|
||||
endif()
|
||||
245
platforms/sdl/SoundSystemAL.cpp
Normal file
245
platforms/sdl/SoundSystemAL.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "SoundSystemAL.hpp"
|
||||
|
||||
#include "client/common/Utils.hpp"
|
||||
|
||||
SoundSystemAL::SoundSystemAL() {
|
||||
device = alcOpenDevice(NULL);
|
||||
if (!device) {
|
||||
LogMsg("Unable To Load Audio Engine");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create Context
|
||||
context = alcCreateContext(device, NULL);
|
||||
ALCenum err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
LogMsg("Unable To Open Audio Context: %s", alcGetString(device, err));
|
||||
return;
|
||||
}
|
||||
|
||||
// Select Context
|
||||
alcMakeContextCurrent(context);
|
||||
err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
LogMsg("Unable To Select Audio Context: %s", alcGetString(device, err));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set Distance Model
|
||||
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
|
||||
|
||||
// Mark As Loaded
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
SoundSystemAL::~SoundSystemAL() {
|
||||
if (loaded) {
|
||||
// Delete Audio Sources
|
||||
delete_sources();
|
||||
|
||||
// Delete Audio Buffers
|
||||
delete_buffers();
|
||||
|
||||
// Deselect Context
|
||||
alcMakeContextCurrent(NULL);
|
||||
ALCenum err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
LogMsg("Unable To Deselect Audio Context: %s", alcGetString(device, err));
|
||||
}
|
||||
|
||||
// Destroy Context
|
||||
alcDestroyContext(context);
|
||||
err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
LogMsg("Unable To Destroy Audio Context: %s", alcGetString(device, err));
|
||||
}
|
||||
|
||||
// Close Device
|
||||
alcCloseDevice(device);
|
||||
err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
LogMsg("Unable To Close Audio Device: %s", alcGetString(device, err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error Checking
|
||||
#define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError())
|
||||
#define AL_ERROR_CHECK_MANUAL(val) \
|
||||
{ \
|
||||
ALenum __err = val; \
|
||||
if (__err != AL_NO_ERROR) { \
|
||||
LogMsg("(%s:%i) OpenAL Error: %s", __FILE__, __LINE__, alGetString(__err)); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
}
|
||||
|
||||
// Delete Sources
|
||||
void SoundSystemAL::delete_sources() {
|
||||
if (loaded) {
|
||||
for (ALuint source : idle_sources) {
|
||||
alDeleteSources(1, &source);
|
||||
AL_ERROR_CHECK();
|
||||
}
|
||||
for (ALuint source : sources) {
|
||||
alDeleteSources(1, &source);
|
||||
AL_ERROR_CHECK();
|
||||
}
|
||||
}
|
||||
idle_sources.clear();
|
||||
sources.clear();
|
||||
}
|
||||
|
||||
// Delete Buffers
|
||||
void SoundSystemAL::delete_buffers() {
|
||||
if (loaded) {
|
||||
for (auto &it : buffers) {
|
||||
if (it.second && alIsBuffer(it.second)) {
|
||||
alDeleteBuffers(1, &it.second);
|
||||
AL_ERROR_CHECK();
|
||||
}
|
||||
}
|
||||
}
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
// Get Buffer
|
||||
ALuint SoundSystemAL::get_buffer(const SoundDesc &sound) {
|
||||
if (buffers.count(sound.m_pData) > 0) {
|
||||
return buffers[sound.m_pData];
|
||||
} else {
|
||||
// Sound Format
|
||||
ALenum format = AL_NONE;
|
||||
if (sound.m_header.m_channels == 1) {
|
||||
format = sound.m_header.m_bytes_per_sample == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8;
|
||||
} else if (sound.m_header.m_channels == 2) {
|
||||
format = sound.m_header.m_bytes_per_sample == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8;
|
||||
}
|
||||
|
||||
// Sound Data Size
|
||||
int size = sound.m_header.m_length * sound.m_header.m_bytes_per_sample;
|
||||
|
||||
// Create Buffer
|
||||
ALuint buffer;
|
||||
alGenBuffers(1, &buffer);
|
||||
AL_ERROR_CHECK();
|
||||
alBufferData(buffer, format, sound.m_pData, size, sound.m_header.m_sample_rate);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Store
|
||||
buffers[sound.m_pData] = buffer;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystemAL::update(float x, float y, float z, float yaw) {
|
||||
// Check
|
||||
if (loaded) {
|
||||
// Update Listener Volume
|
||||
float volume = 1;
|
||||
alListenerf(AL_GAIN, volume);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Update Listener Position
|
||||
alListener3f(AL_POSITION, x, y, z);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Update Listener Orientation
|
||||
float radian_yaw = yaw * (M_PI / 180);
|
||||
ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
|
||||
alListenerfv(AL_ORIENTATION, orientation);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Clear Finished Sources
|
||||
std::vector<ALuint>::iterator it = sources.begin();
|
||||
while (it != sources.end()) {
|
||||
ALuint source = *it;
|
||||
bool remove = false;
|
||||
// Check
|
||||
if (source && alIsSource(source)) {
|
||||
// Is Valid Source
|
||||
ALint source_state;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &source_state);
|
||||
AL_ERROR_CHECK();
|
||||
if (source_state != AL_PLAYING) {
|
||||
// Finished Playing
|
||||
remove = true;
|
||||
if (idle_sources.size() < MAX_IDLE_SOURCES) {
|
||||
idle_sources.push_back(source);
|
||||
} else {
|
||||
alDeleteSources(1, &source);
|
||||
AL_ERROR_CHECK();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not A Source
|
||||
remove = true;
|
||||
}
|
||||
// Remove If Needed
|
||||
if (remove) {
|
||||
it = sources.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystemAL::play(const SoundDesc &sound, float x, float y, float z, float volume, float pitch, bool is_ui) {
|
||||
if (loaded) {
|
||||
// Load Sound
|
||||
ALuint buffer = get_buffer(sound);
|
||||
if (volume > 0.0f && buffer) {
|
||||
// Get Source
|
||||
ALuint al_source;
|
||||
if (idle_sources.size() > 0) {
|
||||
// Use Idle Source
|
||||
al_source = idle_sources.back();
|
||||
idle_sources.pop_back();
|
||||
} else {
|
||||
// Create Source
|
||||
alGenSources(1, &al_source);
|
||||
// Special Out-Of-Memory Handling
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
if (err == AL_OUT_OF_MEMORY) {
|
||||
return;
|
||||
} else {
|
||||
AL_ERROR_CHECK_MANUAL(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Properties
|
||||
alSourcef(al_source, AL_PITCH, pitch);
|
||||
AL_ERROR_CHECK();
|
||||
alSourcef(al_source, AL_GAIN, volume);
|
||||
AL_ERROR_CHECK();
|
||||
alSource3f(al_source, AL_POSITION, x, y, z);
|
||||
AL_ERROR_CHECK();
|
||||
alSource3f(al_source, AL_VELOCITY, 0, 0, 0);
|
||||
AL_ERROR_CHECK();
|
||||
alSourcei(al_source, AL_LOOPING, AL_FALSE);
|
||||
AL_ERROR_CHECK();
|
||||
alSourcei(al_source, AL_SOURCE_RELATIVE, is_ui ? AL_TRUE : AL_FALSE);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Set Attenuation
|
||||
alSourcef(al_source, AL_MAX_DISTANCE, 16.0f);
|
||||
AL_ERROR_CHECK();
|
||||
alSourcef(al_source, AL_ROLLOFF_FACTOR, 6.0f);
|
||||
AL_ERROR_CHECK();
|
||||
alSourcef(al_source, AL_REFERENCE_DISTANCE, 5.0f);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Set Buffer
|
||||
alSourcei(al_source, AL_BUFFER, buffer);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Play
|
||||
alSourcePlay(al_source);
|
||||
AL_ERROR_CHECK();
|
||||
sources.push_back(al_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
platforms/sdl/SoundSystemAL.hpp
Normal file
31
platforms/sdl/SoundSystemAL.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "client/sound/SoundData.hpp"
|
||||
|
||||
#define MAX_IDLE_SOURCES 50
|
||||
|
||||
class SoundSystemAL {
|
||||
public:
|
||||
SoundSystemAL();
|
||||
~SoundSystemAL();
|
||||
void update(float x, float y, float z, float yaw);
|
||||
void play(const SoundDesc &sound, float x, float y, float z, float volume, float pitch, bool is_ui);
|
||||
private:
|
||||
void delete_sources();
|
||||
void delete_buffers();
|
||||
ALuint get_buffer(const SoundDesc &sound);
|
||||
|
||||
ALCdevice *device = NULL;
|
||||
ALCcontext *context = NULL;
|
||||
bool loaded = false;
|
||||
std::vector<ALuint> sources;
|
||||
std::vector<ALuint> idle_sources;
|
||||
std::unordered_map<void *, ALuint> buffers;
|
||||
};
|
||||
258
platforms/sdl/main.cpp
Normal file
258
platforms/sdl/main.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
#include <cstdarg>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "compat/GL.hpp"
|
||||
#include "compat/AKeyCodes.hpp"
|
||||
#include "App.hpp"
|
||||
#include "AppPlatform_sdl.hpp"
|
||||
#include "NinecraftApp.hpp"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
#endif
|
||||
|
||||
void LogMsg(const char* fmt, ...) {
|
||||
va_list lst;
|
||||
va_start(lst, fmt);
|
||||
|
||||
vprintf(fmt, lst);
|
||||
printf("\n");
|
||||
|
||||
va_end(lst);
|
||||
}
|
||||
// I hate duplicating code, but yeah
|
||||
void LogMsgNoCR(const char* fmt, ...) {
|
||||
va_list lst;
|
||||
va_start(lst, fmt);
|
||||
|
||||
vprintf(fmt, lst);
|
||||
|
||||
va_end(lst);
|
||||
}
|
||||
|
||||
AppPlatform_sdl *g_AppPlatform;
|
||||
NinecraftApp *g_pApp;
|
||||
|
||||
SDL_Window *window = NULL;
|
||||
SDL_GLContext context = NULL;
|
||||
static void teardown() {
|
||||
if (window != NULL) {
|
||||
SDL_GL_DeleteContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
window = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Resize From JS
|
||||
#ifdef __EMSCRIPTEN__
|
||||
extern "C" void resize_from_js(int new_width, int new_height) {
|
||||
SDL_SetWindowSize(window, new_width, new_height);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Handle Events
|
||||
static bool window_resized = false;
|
||||
static void handle_events() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
Keyboard::feed(event.key.state == SDL_PRESSED ? 1 : 0, translate_sdl_key_to_mcpe(event.key.keysym.sym));
|
||||
if (event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT) {
|
||||
g_AppPlatform->setShiftPressed(event.key.state == SDL_PRESSED, event.key.keysym.sym == SDLK_LSHIFT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
Mouse::feed(event.button.button == SDL_BUTTON_LEFT ? 1 : 2, event.button.state == SDL_PRESSED ? 1 : 0, event.button.x, event.button.y);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION: {
|
||||
Mouse::_x = event.motion.x;
|
||||
Mouse::_y = event.motion.y;
|
||||
Mouse::feed(0, 0, event.motion.x, event.motion.y);
|
||||
g_AppPlatform->setMouseDiff(event.motion.xrel, event.motion.yrel);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEWHEEL: {
|
||||
Mouse::feed(3, event.wheel.y, Mouse::_x, Mouse::_y);
|
||||
break;
|
||||
}
|
||||
case SDL_TEXTINPUT: {
|
||||
if (g_pApp != nullptr) {
|
||||
char x = event.text.text[0];
|
||||
if (x >= ' ' && x <= '~') {
|
||||
g_pApp->handleCharInput(x);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT: {
|
||||
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||
window_resized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_QUIT: {
|
||||
g_pApp->quit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GUI Scale
|
||||
static void calulate_gui_scale() {
|
||||
int width = Minecraft::width;
|
||||
|
||||
// Modified Version Of https://github.com/MCPI-Revival/Ninecraft/blob/3f71638a10b581f6a50669edb24bc1ef1a92fbea/ninecraft/src/main.c#L243-L255
|
||||
if (width < 1000) {
|
||||
if (width < 400) {
|
||||
Gui::InvGuiScale = 1.0;
|
||||
} else {
|
||||
Gui::InvGuiScale = 0.5;
|
||||
}
|
||||
} else {
|
||||
Gui::InvGuiScale = 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
// Resizing
|
||||
static void resize() {
|
||||
SDL_GL_GetDrawableSize(window, &Minecraft::width, &Minecraft::height);
|
||||
|
||||
calulate_gui_scale();
|
||||
|
||||
if (g_pApp != nullptr && g_pApp->m_pScreen != nullptr) {
|
||||
g_pApp->m_pScreen->setSize(Minecraft::width * Gui::InvGuiScale, Minecraft::height * Gui::InvGuiScale);
|
||||
}
|
||||
}
|
||||
|
||||
// Main Loop
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#define EM_BOOL bool
|
||||
#define EM_TRUE true
|
||||
#define EM_FALSE false
|
||||
#endif
|
||||
static bool is_first_window_resize = true;
|
||||
static EM_BOOL main_loop(double time, void *user_data) {
|
||||
// Handle Events
|
||||
handle_events();
|
||||
|
||||
// Screen Size
|
||||
if (window_resized) {
|
||||
window_resized = false;
|
||||
resize();
|
||||
}
|
||||
|
||||
// Update MCPE
|
||||
g_pApp->update();
|
||||
|
||||
// Swap Buffers
|
||||
SDL_GL_SwapWindow(window);
|
||||
|
||||
if (g_pApp->wantToQuit()) {
|
||||
delete g_pApp;
|
||||
delete g_AppPlatform;
|
||||
teardown();
|
||||
// Stop Looping
|
||||
return EM_FALSE;
|
||||
} else {
|
||||
// Keep Looping
|
||||
return EM_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Main
|
||||
int main(int argc, char *argv[]) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
LOGE("Unable To Initialize SDL: %s\n", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Configure OpenGL ES Context
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
#ifdef USE_GLES1_COMPATIBILITY_LAYER
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
#endif
|
||||
// Double-Buffering
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
// Window Size
|
||||
#ifdef __EMSCRIPTEN__
|
||||
Minecraft::width = std::stoi(argv[1]);
|
||||
Minecraft::height = std::stoi(argv[2]);
|
||||
#endif
|
||||
|
||||
// Create Window
|
||||
window = SDL_CreateWindow("ReMinecraftPE", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Minecraft::width, Minecraft::height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
if (!window) {
|
||||
LOGE("Unable To Create SDL Window\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Enable Text Input
|
||||
SDL_StartTextInput();
|
||||
|
||||
// Create OpenGL ES Context
|
||||
context = SDL_GL_CreateContext(window);
|
||||
if (!context) {
|
||||
LOGE("Unable To Create OpenGL Context\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Setup Compatibility Layer If Needed
|
||||
#ifdef USE_GLES1_COMPATIBILITY_LAYER
|
||||
init_gles_compatibility_layer();
|
||||
#endif
|
||||
|
||||
// Setup Teardown
|
||||
#ifndef __EMSCRIPTEN__
|
||||
atexit(teardown);
|
||||
#endif
|
||||
|
||||
// Set Size
|
||||
resize();
|
||||
|
||||
// Storage Directory
|
||||
#ifdef _WIN32
|
||||
std::string storagePath = getenv("APPDATA");
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
std::string storagePath = "";
|
||||
#else
|
||||
std::string storagePath = getenv("HOME");
|
||||
#endif
|
||||
storagePath += "/.reminecraftpe";
|
||||
ensure_screenshots_folder(storagePath.c_str());
|
||||
|
||||
// Start MCPE
|
||||
g_pApp = new NinecraftApp;
|
||||
g_pApp->m_externalStorageDir = storagePath;
|
||||
g_AppPlatform = new AppPlatform_sdl(g_pApp->m_externalStorageDir, window);
|
||||
g_pApp->m_pPlatform = g_AppPlatform;
|
||||
g_pApp->init();
|
||||
|
||||
// Loop
|
||||
#ifndef __EMSCRIPTEN__
|
||||
while (true) {
|
||||
EM_BOOL result = main_loop(0, nullptr);
|
||||
if (result == EM_FALSE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
emscripten_request_animation_frame_loop(main_loop, nullptr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
54
platforms/sdl/wasm_shell.html
Normal file
54
platforms/sdl/wasm_shell.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>ReMinecraftPE</title>
|
||||
<style>
|
||||
html, body {
|
||||
background-color: black;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
border: 0px none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
|
||||
<script type="text/javascript" src="coi-serviceworker.min.js"></script>
|
||||
<script type='text/javascript'>
|
||||
var Module = {
|
||||
print: function (text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.log(text);
|
||||
},
|
||||
printErr: function (text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
canvas: (function () {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
// Handle Resize
|
||||
function setCanvasSize() {
|
||||
Module.ccall('resize_from_js', null, ['number', 'number'], [window.innerWidth, window.innerHeight]);
|
||||
}
|
||||
window.addEventListener("resize", setCanvasSize);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
arguments: [String(window.innerWidth), String(window.innerHeight)]
|
||||
};
|
||||
</script>
|
||||
<script async type="text/javascript" src="reminecraftpe.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user