ADD: WebASM port (no sound/no network)

This commit is contained in:
Kolyah35
2026-03-19 02:26:34 +03:00
parent 4769d4ae72
commit e9914e3fbd
13 changed files with 408 additions and 119 deletions

View File

@@ -11,48 +11,69 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
endif()
find_package(Threads REQUIRED)
find_package(OpenSSL)
if (OpenSSL_FOUND)
message(STATUS "found openssl ${OPENSSL_VERSION}")
if(EMSCRIPTEN)
set(AL_LIBTYPE "STATIC")
else()
set(AL_LIBTYPE "SHARED")
endif()
CPMAddPackage("gh:madler/zlib@1.3.2")
CPMAddPackage(
NAME "libpng"
GIT_REPOSITORY "https://github.com/pnggroup/libpng.git"
GIT_TAG "v1.6.55"
EXCLUDE_FROM_ALL TRUE
OPTIONS
"ZLIB_ROOT ${zlib_SOURCE_DIR}"
"ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR}"
"PNG_TOOLS OFF"
"PNG_TESTS OFF"
"BUILD_SHARED_LIBS ON"
)
# I totally shocked
if(EMSCRIPTEN)
add_library(zlib INTERFACE IMPORTED)
set_target_properties(zlib PROPERTIES
INTERFACE_LINK_OPTIONS "-sUSE_ZLIB=1"
)
add_library(png INTERFACE IMPORTED)
set_target_properties(png PROPERTIES
INTERFACE_LINK_OPTIONS "-sUSE_LIBPNG=1"
)
add_library(glfw INTERFACE IMPORTED)
set_target_properties(glfw PROPERTIES
INTERFACE_LINK_OPTIONS "-sUSE_GLFW=3"
)
else()
CPMAddPackage(
NAME "zlib"
GIT_REPOSITORY "https://github.com/madler/zlib"
GIT_TAG "v1.3.2"
)
CPMAddPackage(
NAME "libpng"
GIT_REPOSITORY "https://github.com/pnggroup/libpng.git"
GIT_TAG "v1.6.55"
OPTIONS
"ZLIB_ROOT ${zlib_SOURCE_DIR}"
"ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR}"
"PNG_TOOLS OFF"
"PNG_TESTS OFF"
)
CPMAddPackage(
NAME "glfw"
GIT_REPOSITORY "https://github.com/glfw/glfw.git"
GIT_TAG "3.4"
EXCLUDE_FROM_ALL TRUE
OPTIONS
"GLFW_BUILD_EXAMPLES OFF"
"GLFW_BUILD_TESTS OFF"
"GLFW_BUILD_DOCS OFF"
)
endif()
CPMAddPackage(
NAME "openal"
GIT_REPOSITORY "https://github.com/kcat/openal-soft.git"
GIT_TAG "1.25.1"
EXCLUDE_FROM_ALL TRUE
OPTIONS
"ALSOFT_EXAMPLES OFF"
"ALSOFT_TESTS OFF"
"ALSOFT_UTILS OFF"
"BUILD_SHARED_LIBS ON"
)
CPMAddPackage(
NAME "glfw"
GIT_REPOSITORY "https://github.com/glfw/glfw.git"
GIT_TAG "3.4"
EXCLUDE_FROM_ALL TRUE
OPTIONS
"GLFW_BUILD_EXAMPLES OFF"
"GLFW_BUILD_TESTS OFF"
"GLFW_BUILD_DOCS OFF"
"BUILD_SHARED_LIBS ON"
"LIBTYPE ${AL_LIBTYPE}"
)
# TODO: Clear this paths with *
@@ -242,11 +263,15 @@ endif()
if(PLATFORM STREQUAL "PLATFORM_WIN32")
list(APPEND CLIENT_SOURCES "src/AppPlatform_win32.cpp")
list(APPEND CLIENT_SOURCES "src/AppPlatform_win32.cpp" "glad/src/glad.c")
endif()
if(PLATFORM STREQUAL "PLATFORM_GLFW")
list(APPEND CLIENT_SOURCES "src/AppPlatform_glfw.cpp")
list(APPEND CLIENT_SOURCES "src/AppPlatform_glfw.cpp" "glad/src/glad.c")
endif()
if(EMSCRIPTEN)
list(APPEND CLIENT_SOURCES "glad/src/glad.c")
endif()
# Server
@@ -260,13 +285,10 @@ target_include_directories("${PROJECT_NAME}-server" PUBLIC
"project/lib_projects/raknet/jni/RaknetSources"
)
target_link_libraries("${PROJECT_NAME}-server" ${CMAKE_THREAD_LIBS_INIT} png_shared)
target_link_libraries("${PROJECT_NAME}-server" ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable(${PROJECT_NAME}
${CLIENT_SOURCES}
"glad/src/glad.c"
)
add_executable(${PROJECT_NAME} ${CLIENT_SOURCES})
if(WIN32)
set(EXTRA_LIBS "ws2_32")
@@ -277,8 +299,6 @@ if(PLATFORM STREQUAL "PLATFORM_WIN32" OR PLATFORM STREQUAL "PLATFORM_GLFW")
target_compile_definitions(${PROJECT_NAME} PUBLIC "PLATFORM_DESKTOP")
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"${CMAKE_SOURCE_DIR}/glad/include/"
"${CMAKE_SOURCE_DIR}/src"
@@ -287,9 +307,53 @@ target_include_directories(${PROJECT_NAME} PUBLIC
"lib/include"
)
if(EMSCRIPTEN)
set(CMAKE_CXX_STANDARD 11)
# uuuh i hate it
set(EM_FLAGS "-pthread -sUSE_PTHREADS=1 -sSHARED_MEMORY=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EM_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EM_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EM_FLAGS} --preload-file ${CMAKE_SOURCE_DIR}/data@/data -sPROXY_TO_PTHREAD")
target_compile_options(${PROJECT_NAME} PUBLIC
"-Os"
"-Wno-invalid-source-encoding"
"-Wno-narrowing"
"-Wno-deprecated-register"
"-Wno-reserved-user-defined-literal"
)
target_link_options(${PROJECT_NAME} PUBLIC
"-Os"
"-sALLOW_MEMORY_GROWTH=1"
"-sFORCE_FILESYSTEM=1"
"-sLEGACY_GL_EMULATION=1"
"-sGL_UNSAFE_OPTS=0"
"-sEMULATE_FUNCTION_POINTER_CASTS=1"
"-sALLOW_TABLE_GROWTH=1"
"-sEXPORTED_RUNTIME_METHODS=['FS','stringToUTF8','UTF8ToString','cwrap','ccall','HEAP8','HEAPU8','HEAP32','HEAPU32']"
"-sEXPORTED_FUNCTIONS=['_main']"
)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
message("DEBUG MODE")
target_link_options(${PROJECT_NAME} PUBLIC
"-sASSERTIONS=2"
"-sSTACK_OVERFLOW_CHECK=2"
"-sSTACK_SIZE=5242880"
"-sGL_DEBUG=1"
)
endif()
target_compile_definitions(${PROJECT_NAME} PUBLIC "__EMSCRIPTEN__" "NO_SOUND" "NO_NETWORK")
set(EXTRA_LIBS "idbfs.js")
endif()
# Client
target_compile_definitions(${PROJECT_NAME} PUBLIC "OPENGL_ES" "NO_EGL" ${PLATFORM})
target_link_libraries(${PROJECT_NAME} zlib png_shared alsoft.common OpenAL::OpenAL glfw ${EXTRA_LIBS})
target_link_libraries(${PROJECT_NAME} zlib png alsoft.common OpenAL::OpenAL glfw ${EXTRA_LIBS})
if (OpenSSL_FOUND)
target_link_libraries(${PROJECT_NAME} OpenSSL::SSL OpenSSL::Crypto)
@@ -297,19 +361,27 @@ if (OpenSSL_FOUND)
endif()
if (NOT UNIX)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:${PROJECT_NAME}> $<TARGET_FILE_DIR:${PROJECT_NAME}>
COMMAND_EXPAND_LISTS
)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:${PROJECT_NAME}> $<TARGET_FILE_DIR:${PROJECT_NAME}>
COMMAND_EXPAND_LISTS
)
endif()
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/data" $<TARGET_FILE_DIR:${PROJECT_NAME}>/data
)
if(NOT EMSCRIPTEN)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/data" $<TARGET_FILE_DIR:${PROJECT_NAME}>/data
)
else()
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/misc/web/index.html" $<TARGET_FILE_DIR:${PROJECT_NAME}>
)
endif()
# Installing and packing

View File

@@ -167,3 +167,34 @@ cmake --build .
8. Re run `build.sh`
## Web
1. Download and install **emsdk**: https://emscripten.org/docs/getting_started/downloads.html
> [!NOTE]
> On arch linux you can use AUR:
> `yay -Sy emsdk`
2. Configure and build project:
```
mkdir build && cd build
cmake .. -B . -G Ninja "-DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
cmake --build . --target MinecraftPE
```
> [!NOTE]
> If you are using VSCode with CMake plugin, you can add Emscripten kit
> 1. Press Ctrl + Shift + P
> 2. Type `CMake: Edit User-Local CMake Kits` and hit Enter
> 3. Add this:
```json
{
"name": "Emscripten",
"compilers": {
"C": "/usr/lib/emsdk/upstream/bin/clang",
"CXX": "/usr/lib/emsdk/upstream/bin/clang++"
},
"toolchainFile": "/usr/lib/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
}
```
3. Run game:
```
emrun --port 8080 .
```

143
misc/web/index.html Normal file
View File

@@ -0,0 +1,143 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
textarea.emscripten { font-family: monospace; width: 80%; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; background-color: black; }
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
</style>
</head>
<body>
<hr/>
<figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>
<hr/>
<div class="emscripten">
<input type="checkbox" id="resize">Resize canvas
<input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
&nbsp;&nbsp;&nbsp;
<input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
document.getElementById('resize').checked)">
</div>
<hr/>
<textarea class="emscripten" id="output" rows="8"></textarea>
<hr>
<script type='text/javascript'>
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');
var canvasElement = document.getElementById('canvas');
var outputElement = document.getElementById('output');
if (outputElement) outputElement.value = ''; // clear browser cache
// 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
canvasElement.addEventListener("webglcontextlost", (e) => {
alert('WebGL context lost. You will need to reload the page.');
e.preventDefault();
}, false);
function setStatus(text) {
if (!setStatus.last) setStatus.last = { time: Date.now(), text: '' };
if (text === setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
setStatus.last.time = now;
setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
}
var Module = {
print(...args) {
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
console.log(...args);
if (outputElement) {
var text = args.join(' ');
outputElement.value += text + "\n";
outputElement.scrollTop = outputElement.scrollHeight; // focus on bottom
}
},
canvas: canvasElement,
setStatus: setStatus,
totalDependencies: 0,
monitorRunDependencies(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
setStatus('Downloading...');
window.onerror = () => {
setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
setStatus = (text) => {
if (text) console.error('[post-exception status] ' + text);
};
};
</script>
<script src="MinecraftPE.js"></script>
</body>
</html>

View File

@@ -31,7 +31,7 @@ public:
{
}
BinaryBlob readAssetFile(const std::string& filename) {
BinaryBlob readAssetFile(const std::string& filename) override {
FILE* fp = fopen(("data/" + filename).c_str(), "r");
if (!fp)
return BinaryBlob();
@@ -48,7 +48,7 @@ public:
return blob;
}
void saveScreenshot(const std::string& filename, int glWidth, int glHeight) {
void saveScreenshot(const std::string& filename, int glWidth, int glHeight) override {
//@todo
}
@@ -56,7 +56,7 @@ public:
return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000);
}
TextureData loadTexture(const std::string& filename_, bool textureFolder)
TextureData loadTexture(const std::string& filename_, bool textureFolder) override
{
// Support fetching PNG textures via HTTP/HTTPS (for skins, etc)
if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) {
@@ -132,10 +132,10 @@ public:
return std::string(mbstr);
}
virtual int getScreenWidth() { return 854; };
virtual int getScreenHeight() { return 480; };
virtual int getScreenWidth() override { return 854; };
virtual int getScreenHeight() override { return 480; };
virtual float getPixelsPerMillimeter();
virtual float getPixelsPerMillimeter() override;
virtual bool supportsTouchscreen() override { return true; }
@@ -148,6 +148,8 @@ public:
#endif
}
GLFWwindow* window;
private:
};
#endif /*APPPLATFORM_GLFW_H__*/

View File

@@ -245,8 +245,9 @@ void NinecraftApp::initGLStates()
glCullFace(GL_BACK);
glEnable2(GL_TEXTURE_2D);
#ifndef _EMSCRIPTEN_
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
#endif
// Both updates isPowerVR flag in java and returns if the graphics chip is PowerVR SGX or not
_powerVr = platform()->isPowerVR();
#ifdef __APPLE__

View File

@@ -37,6 +37,8 @@ void CreditsScreen::init() {
_lines.push_back("mschiller890");
_lines.push_back("InviseDivine");
_lines.push_back("Kolyah35");
_lines.push_back("karson");
_lines.push_back("deepfriedwaffles");
_lines.push_back("");
// avoid color tags around the URL so it isn't mangled by the parser please
_lines.push_back("Join our Discord server: https://discord.gg/c58YesBxve");

View File

@@ -86,21 +86,21 @@ public:
SelectWorldScreen();
virtual ~SelectWorldScreen();
virtual void init();
virtual void setupPositions();
virtual void init() override;
virtual void setupPositions() override;
virtual void tick();
virtual void render(int xm, int ym, float a);
virtual void tick() override;
virtual void render(int xm, int ym, float a) override;
virtual bool isIndexValid(int index);
virtual bool handleBackEvent(bool isDown);
virtual void buttonClicked(Button* button);
virtual void keyPressed(int eventKey);
virtual bool handleBackEvent(bool isDown) override;
virtual void buttonClicked(Button* button) override;
virtual void keyPressed(int eventKey) override;
// support for mouse wheel when desktop code uses touch variant
virtual void mouseWheel(int dx, int dy, int xm, int ym) override;
bool isInGameScreen();
bool isInGameScreen() override;
private:
void loadLevelSource();
std::string getUniqueLevelName(const std::string& level);

View File

@@ -79,6 +79,9 @@ RenderChunk Tesselator::end( bool useMine, int bufferId )
const int o_vertices = vertices;
if (vertices > 0) {
if (p <= 0 || p > maxVertices) { clear(); return RenderChunk(); }
int bytes = p * sizeof(VERTEX);
if (bytes <= 0) return RenderChunk();
if (++vboId >= vboCounts)
vboId = 0;
@@ -92,11 +95,11 @@ RenderChunk Tesselator::end( bool useMine, int bufferId )
bufferId = vboIds[vboId];
#endif
int access = GL_STATIC_DRAW;//(accessMode==ACCESS_DYNAMIC) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
int bytes = p * sizeof(VERTEX);
glBindBuffer2(GL_ARRAY_BUFFER, bufferId);
glBufferData2(GL_ARRAY_BUFFER, bytes, _varray, access); // GL_STREAM_DRAW
totalSize += bytes;
#ifndef USE_VBO
// 0 1 2 3 4 5 6 7
// x y z u v c
@@ -264,6 +267,7 @@ void Tesselator::vertex( float x, float y, float z )
for (int i = 0; i < 2; i++) {
const int offs = 3 - i;
if (p - offs < 0 || p >= maxVertices) { clear(); return; }
VERTEX& src = _varray[p - offs];
VERTEX& dst = _varray[p];
@@ -287,6 +291,7 @@ void Tesselator::vertex( float x, float y, float z )
}
}
if (p < 0 || p >= maxVertices) { clear(); return; }
VERTEX& vertex = _varray[p];
if (hasTexture) {
@@ -377,13 +382,15 @@ void Tesselator::draw()
tesselating = false;
if (vertices > 0) {
if (p <= 0 || p > maxVertices) { clear(); return; }
int bytes = p * sizeof(VERTEX);
if (bytes <= 0) { clear(); return; }
if (++vboId >= vboCounts)
vboId = 0;
int bufferId = vboIds[vboId];
int access = GL_DYNAMIC_DRAW;//(accessMode==ACCESS_DYNAMIC) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
int bytes = p * sizeof(VERTEX);
glBindBuffer2(GL_ARRAY_BUFFER, bufferId);
glBufferData2(GL_ARRAY_BUFFER, bytes, _varray, access); // GL_STREAM_DRAW

View File

@@ -47,9 +47,13 @@ void glInit()
}
void anGenBuffers(GLsizei n, GLuint* buffers) {
static GLuint k = 1;
for (int i = 0; i < n; ++i)
buffers[i] = ++k;
#ifdef __EMSCRIPTEN__
glGenBuffers(n, buffers);
#else
static GLuint k = 1;
for (int i = 0; i < n; ++i)
buffers[i] = ++k;
#endif
}
#ifdef USE_VBO

View File

@@ -10,13 +10,13 @@
#endif
// Other systems might run it, if they #define OPENGL_ES
#if defined(OPENGL_ES) // || defined(ANDROID)
// #if defined(OPENGL_ES) // || defined(ANDROID)
#define USE_VBO
#define GL_QUADS 0x0007
#if defined(__APPLE__)
#import <OpenGLES/ES1/gl.height>
#import <OpenGLES/ES1/glext.height>
#elif defined(ANDROID)
#elif defined(ANDROID) || defined(__EMSCRIPTEN__)
#include <GLES/gl.h>
#include <GLES/glext.h>
#else
@@ -28,18 +28,18 @@
#define glClearDepthf(x) glClearDepth(x)
#define glDepthRangef(a,b) glDepthRange(a,b)
#endif
#else
// Uglyness to fix redeclaration issues
#ifdef WIN32
#include <WinSock2.h>
#include <Windows.h>
#endif
#include <gl/glew.h>
#include <gl/GL.h>
// #else
// // Uglyness to fix redeclaration issues
// #ifdef WIN32
// #include <WinSock2.h>
// #include <Windows.h>
// #endif
// #include <gl/glew.h>
// #include <gl/GL.h>
#define glFogx(a,b) glFogi(a,b)
#define glOrthof(a,b,c,d,e,f) glOrtho(a,b,c,d,e,f)
#endif
// #define glFogx(a,b) glFogi(a,b)
// #define glOrthof(a,b,c,d,e,f) glOrtho(a,b,c,d,e,f)
// #endif
#define GLERRDEBUG 1

View File

@@ -2,9 +2,9 @@
#define MAIN_GLFW_H__
#include "App.h"
#include "GLFW/glfw3.h"
#include "client/renderer/entity/PlayerRenderer.h"
#include "client/renderer/gles.h"
#include "SharedConstants.h"
#include "GLFW/glfw3.h"
#include <cstdio>
#include <chrono>
@@ -12,9 +12,10 @@
#include "platform/input/Keyboard.h"
#include "platform/input/Mouse.h"
#include "platform/input/Multitouch.h"
#include "util/Mth.h"
#include "AppPlatform_glfw.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
static App* g_app = 0;
int transformKey(int glfwkey) {
@@ -112,12 +113,39 @@ void error_callback(int error, const char* desc) {
printf("Error: %s\n", desc);
}
void loop() {
using clock = std::chrono::steady_clock;
auto frameStart = clock::now();
g_app->update();
glfwSwapBuffers(((AppPlatform_glfw*)g_app->platform())->window);
glfwPollEvents();
glfwSwapInterval(((MAIN_CLASS*)g_app)->options.getBooleanValue(OPTIONS_VSYNC) ? 1 : 0);
if(((MAIN_CLASS*)g_app)->options.getBooleanValue(OPTIONS_LIMIT_FRAMERATE)) {
auto frameEnd = clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(frameEnd - frameStart);
auto target = std::chrono::microseconds(33333); // ~30 fps
if(elapsed < target)
std::this_thread::sleep_for(target - elapsed);
}
}
int main(void) {
AppContext appContext;
#ifndef STANDALONE_SERVER
// Platform init.
appContext.platform = new AppPlatform_glfw();
#if defined(DEBUG) && defined(__EMSCRIPTEN__)
EM_ASM({
console.log(FS.readdir("/"));
console.log(FS.readdir("/data"));
console.log(FS.readdir("/data/images"));
});
#endif
glfwSetErrorCallback(error_callback);
@@ -126,26 +154,36 @@ int main(void) {
}
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
#ifndef __EMSCRIPTEN__
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
#else
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
GLFWwindow* window = glfwCreateWindow(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight(), "main", NULL, NULL);
AppPlatform_glfw* platform = (AppPlatform_glfw*)appContext.platform;
platform->window = glfwCreateWindow(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight(), "main", NULL, NULL);
if (window == NULL) {
if (platform->window == NULL) {
return 1;
}
glfwSetKeyCallback(window, key_callback);
glfwSetCharCallback(window, character_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetWindowSizeCallback(window, window_size_callback);
glfwSetKeyCallback(platform->window, key_callback);
glfwSetCharCallback(platform->window, character_callback);
glfwSetCursorPosCallback(platform->window, cursor_position_callback);
glfwSetMouseButtonCallback(platform->window, mouse_button_callback);
glfwSetScrollCallback(platform->window, scroll_callback);
glfwSetWindowSizeCallback(platform->window, window_size_callback);
glfwMakeContextCurrent(window);
glfwMakeContextCurrent(platform->window);
#ifndef __EMSCRIPTEN__
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glfwSwapInterval(0);
#endif
#endif
App* app = new MAIN_CLASS();
@@ -156,25 +194,14 @@ int main(void) {
g_app->init(appContext);
g_app->setSize(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight());
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(loop, 0, 1);
#else
// Main event loop
using clock = std::chrono::steady_clock;
while(!glfwWindowShouldClose(window) && !app->wantToQuit()) {
auto frameStart = clock::now();
app->update();
glfwSwapBuffers(window);
glfwPollEvents();
glfwSwapInterval(((MAIN_CLASS*)app)->options.getBooleanValue(OPTIONS_VSYNC) ? 1 : 0);
if(((MAIN_CLASS*)app)->options.getBooleanValue(OPTIONS_LIMIT_FRAMERATE)) {
auto frameEnd = clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(frameEnd - frameStart);
auto target = std::chrono::microseconds(33333); // ~30 fps
if(elapsed < target)
std::this_thread::sleep_for(target - elapsed);
}
while(!glfwWindowShouldClose(platform->window) && !app->wantToQuit()) {
loop();
}
#endif
delete app;
@@ -184,7 +211,7 @@ int main(void) {
#ifndef STANDALONE_SERVER
// Exit.
glfwDestroyWindow(window);
glfwDestroyWindow(platform->window);
glfwTerminate();
#endif

View File

@@ -25,7 +25,7 @@
&m_threadID // pointer to receive thread ID
);
#endif
#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX)
#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__)
mp_threadFunc = (pthread_fn)threadFunc;
pthread_attr_init(&m_attributes);

View File

@@ -13,7 +13,7 @@
typedef void *( * pthread_fn )( void * );
#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX)
#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__)
#include <pthread.h>
#include <unistd.h>
@@ -38,7 +38,7 @@ typedef void *( * pthread_fn )( void * );
DWORD m_threadID;
HANDLE m_threadHandle;
#endif
#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX)
#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__)
pthread_fn mp_threadFunc;
pthread_t m_thread;
pthread_attr_t m_attributes;