diff --git a/.gitignore b/.gitignore index e595637..636ea36 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ !skeleton.html !meson.build !meson_options.txt -!compile_wasm.py \ No newline at end of file +!compile_wasm.py +!compile_android.py \ No newline at end of file diff --git a/compile_android.py b/compile_android.py new file mode 100644 index 0000000..15f04ea --- /dev/null +++ b/compile_android.py @@ -0,0 +1,78 @@ +import os +import ntpath +import sys + +def compile(sources, output): + files = [] + # r=root, d=directories, f = files + for path in sources: + for r, d, f in os.walk(path): + for file in f: + if ntpath.basename(file) != 'win_dll.d': + filename, file_extension = os.path.splitext(file) + if file_extension == '.d' and filename != 'package': + files.append(os.path.join(r, file)) + + ldc_path = 'ldc' + if 'LDC' in os.environ: + ldc_path = os.environ['LDC'] + + ldc_cmd = ldc_path + ' ' + ldc_flags + '-lib -mtriple=armv7-none-linux-androideabi -fvisibility=hidden -betterC -oq -od=obj/ --singleobj --of=' + output + ' ' + + for path in sources: + ldc_cmd += '-I' + path + ' ' + + for path in import_paths: + ldc_cmd += '-I' + path + ' ' + + for f in files: + ldc_cmd += f + ' ' + + print(ldc_cmd) + + if os.system(ldc_cmd): + exit(0) + print() + +clean = 0 +ldc_flags = '' +import_paths = ['source','tests'] +build_tests = 0 + +for arg in sys.argv[1:]: + if(arg == '-release'): + ldc_flags += '-release ' + elif(arg == '-enable-inlining'): + ldc_flags += '-enable-inlining ' + elif(arg == '-O3'): + ldc_flags += '-O3 ' + elif(arg == '-O2'): + ldc_flags += '-O2 ' + elif(arg == '-O1'): + ldc_flags += '-O1 ' + elif(arg == '-O0'): + ldc_flags += '-O0 ' + elif(arg == '-Os'): + ldc_flags += '-Os ' + elif(arg == '-Oz'): + ldc_flags += '-Oz ' + elif(arg == '-g'): + ldc_flags += '-g ' + elif(arg == '-opt'): + ldc_flags += '-release -enable-inlining -O3 ' + else: + print('unknown argument: ' + arg) + exit() + +compile(['source'], 'ecs.a') + +#export LDC_LIBS=/path/to/your/ldc-build-runtime.tmp/lib/ +CC = os.environ['NDK'] + '/toolchains/llvm/prebuilt/linux-x86_64/bin/clang' +TOOLCHAIN = os.environ['NDK'] + '/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64' +SYSROOT = os.environ['NDK'] + '/platforms/android-21/arch-arm' +LDC_LIBS = ''#os.environ['LDC_LIBS'] + '/libphobos2-ldc.a ' + os.environ['LDC_LIBS'] + '/libdruntime-ldc.a' + +os.system(CC + ' -Wl,-soname,libecs.so -shared --sysroot=' + SYSROOT + ' obj/*.o ' + LDC_LIBS + ' -lgcc -gcc-toolchain ' + TOOLCHAIN + +' -no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi -fvisibility=hidden \ +-Wl,--gc-sections -Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro \ +-Wl,-z,now -mthumb -o libecs.so') diff --git a/demos/.gitignore b/demos/.gitignore index 1ac2a4a..d74e2b0 100644 --- a/demos/.gitignore +++ b/demos/.gitignore @@ -14,4 +14,6 @@ !cimgui.bc !emscripten_shell.html !emscripten_multi_shell.html -.dub \ No newline at end of file +!compile_android.py +.dub +Android \ No newline at end of file diff --git a/demos/assets/shaders/additive_particles.fp b/demos/assets/shaders/additive_particles.fp new file mode 100644 index 0000000..047f647 --- /dev/null +++ b/demos/assets/shaders/additive_particles.fp @@ -0,0 +1,55 @@ +precision mediump int; +precision mediump float; +precision lowp sampler2D; +precision lowp samplerCube; + + +#ifdef GLES + #define TEX(x,y) texture2D(x,y) + #if __VERSION__ >290 + #define M_IN in mediump + #define L_IN in lowp + #else + #define M_IN varying mediump + #define L_IN varying lowp + #endif +#else + #define TEX(x,y) texture(x,y) + #if __VERSION__ > 320 + #define M_IN in + #define L_IN in + #else + #define M_IN varying + #define L_IN varying + #endif +#endif + + +M_IN vec2 uv; +M_IN vec4 color; +/* +#ifdef GLES + #if __VERSION__ >290 + in mediump vec2 uv; + #else + varying mediump vec2 uv; + #endif +#else + #if __VERSION__ > 320 + in vec2 uv; + #else + varying vec2 uv; + #endif +#endif*/ + +//layout(binding = 0)uniform sampler2D tex; + +uniform sampler2D tex; + +//layout(location = 0) out vec4 outColor; + +void main() +{ + gl_FragColor = /*TEX(tex,uv) **/ color; + if(gl_FragColor.a < 0.01)discard; +} diff --git a/demos/assets/shaders/additive_particles.vp b/demos/assets/shaders/additive_particles.vp new file mode 100644 index 0000000..2777a24 --- /dev/null +++ b/demos/assets/shaders/additive_particles.vp @@ -0,0 +1,106 @@ +precision highp float; +precision highp int; +precision lowp sampler2D; +precision lowp samplerCube; +#ifdef GLES + #if __VERSION__ >290 + #define LOC(x) layout(location = x) + #define ATT in + #define M_OUT out mediump + #define L_OUT out lowp + #else + #define LOC(x) + #define ATT attribute + #define M_OUT varying mediump + #define L_OUT varying lowp + #endif +#else + #if __VERSION__ > 320 + #define LOC(x) layout(location = x) + #define ATT in + #define M_OUT out + #define L_OUT out + #else + #define LOC(x) + #define ATT attribute + #define M_OUT varying + #define L_OUT varying + #endif +#endif +/* +#ifdef GLES + #if __VERSION__ >290 + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + layout(location = 0) in vec2 positions; + layout(location = 1) in vec2 tex_coords; + + out mediump vec2 uv; + #else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + attribute vec2 positions; + attribute vec2 tex_coords; + + varying mediump vec2 uv; + #endif +#else + #if __VERSION__ > 320 + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + layout(location = 0) in vec2 positions; + layout(location = 1) in vec2 tex_coords; + + out vec2 uv; + #else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + attribute vec2 positions; + attribute vec2 tex_coords; + + varying vec2 uv; + #endif +#endif*/ + +#define VBO_BATCH 1 + +M_OUT vec2 uv; +L_OUT vec4 color; + +LOC(0) ATT vec2 positions; +LOC(1) ATT vec2 tex_coords; + +#ifdef VBO_BATCH + LOC(2) ATT float depth; + LOC(3) ATT vec4 vcolor; +#else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + uniform vec4 vcolor; + + float depth = matrix_2.z; +#endif + +void main() { + #ifdef VBO_BATCH + vec3 position = vec3(positions*4.0,1.0); + uv = tex_coords; + #else + vec3 position = mat3(matrix_1.x,matrix_1.y,0,matrix_1.z,matrix_1.w,0,matrix_2.xy,1.0) * vec3(positions,1.0); + uv = tex_coords * uv_transform.zw + uv_transform.xy; + #endif + + color = vcolor; + + gl_Position = vec4(position.xy,depth,1.0); + +} diff --git a/demos/assets/shaders/circle.fp b/demos/assets/shaders/circle.fp new file mode 100644 index 0000000..15f46fd --- /dev/null +++ b/demos/assets/shaders/circle.fp @@ -0,0 +1,64 @@ +precision mediump int; +precision mediump float; +precision lowp sampler2D; +precision lowp samplerCube; + + +#ifdef GLES + #define TEX(x,y) texture2D(x,y) + #if __VERSION__ >290 + #define M_IN in mediump + #define L_IN in lowp + #else + #define M_IN varying mediump + #define L_IN varying lowp + #endif +#else + #define TEX(x,y) texture(x,y) + #if __VERSION__ > 320 + #define M_IN in + #define L_IN in + #else + #define M_IN varying + #define L_IN varying + #endif +#endif + +M_IN vec2 pos; +M_IN float edge; +//flat M_IN vec2 fpos; + +//M_IN vec2 uv; +//M_IN vec4 color; +/* +#ifdef GLES + #if __VERSION__ >290 + in mediump vec2 uv; + #else + varying mediump vec2 uv; + #endif +#else + #if __VERSION__ > 320 + in vec2 uv; + #else + varying vec2 uv; + #endif +#endif*/ + +//layout(binding = 0)uniform sampler2D tex; + +//uniform sampler2D tex; + +//layout(location = 0) out vec4 outColor; + +void main() +{ + float len2 = dot(pos,pos); + + if(len2 > 1.0)discard; + + if(len2 > edge)gl_FragColor = vec4(0.4,0.8,1.0,0.8);//TEX(tex,uv) * color; + else gl_FragColor = vec4(0,0.6,1.0,0.35);//TEX(tex,uv) * color; + //gl_FragColor = vec4(pos,0,1); + //if(gl_FragColor.a < 0.01)discard; +} diff --git a/demos/assets/shaders/circle.vp b/demos/assets/shaders/circle.vp new file mode 100644 index 0000000..6997121 --- /dev/null +++ b/demos/assets/shaders/circle.vp @@ -0,0 +1,114 @@ +precision highp float; +precision highp int; +precision lowp sampler2D; +precision lowp samplerCube; +#ifdef GLES + #if __VERSION__ >290 + #define LOC(x) layout(location = x) + #define ATT in + #define M_OUT out mediump + #define L_OUT out lowp + #else + #define LOC(x) + #define ATT attribute + #define M_OUT varying mediump + #define L_OUT varying lowp + #endif +#else + #if __VERSION__ > 320 + #define LOC(x) layout(location = x) + #define ATT in + #define M_OUT out + #define L_OUT out + #else + #define LOC(x) + #define ATT attribute + #define M_OUT varying + #define L_OUT varying + #endif +#endif +/* +#ifdef GLES + #if __VERSION__ >290 + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + layout(location = 0) in vec2 positions; + layout(location = 1) in vec2 tex_coords; + + out mediump vec2 uv; + #else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + attribute vec2 positions; + attribute vec2 tex_coords; + + varying mediump vec2 uv; + #endif +#else + #if __VERSION__ > 320 + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + layout(location = 0) in vec2 positions; + layout(location = 1) in vec2 tex_coords; + + out vec2 uv; + #else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + uniform vec4 uv_transform; + + attribute vec2 positions; + attribute vec2 tex_coords; + + varying vec2 uv; + #endif +#endif*/ + +//#define VBO_BATCH 1 + +//M_OUT vec2 uv; +//L_OUT vec4 color; +M_OUT vec2 pos; +M_OUT float edge; +//flat M_OUT vec2 fpos; + +LOC(0) ATT vec2 positions; +//LOC(1) ATT vec2 tex_coords; + +#ifdef VBO_BATCH + LOC(2) ATT float depth; + LOC(3) ATT vec4 vcolor; +#else + uniform vec4 matrix_1; + uniform vec4 matrix_2; + //uniform vec4 uv_transform; + //uniform vec4 vcolor; + + //float depth = matrix_2.z; +#endif + +void main() { + //#ifdef VBO_BATCH + // vec3 position = vec3(positions*4.0,1.0); + // uv = tex_coords; + //#else + //edge = mix(0.1, 0.96, (matrix_2.z / 256)); + edge = (matrix_1.w - matrix_2.z) / matrix_1.w;//matrix_2.z;//clamp((matrix_2,0.0,1.0); + edge *= edge; + pos = positions * 2.0;// / matrix_2.zw * 2; + //fpos = positions * matrix_2.xy; + vec3 position = mat3(matrix_1.x,matrix_1.y,0,matrix_1.z,matrix_1.w,0,matrix_2.xy,1.0) * vec3(positions,1.0); + // uv = tex_coords * uv_transform.zw + uv_transform.xy; + //#endif + + //color = vcolor * 2.0; + + gl_Position = vec4(position.xy,0,1.0); + +} diff --git a/demos/assets/textures/atlas.png b/demos/assets/textures/atlas.png index a799eb7..b84c3fa 100644 Binary files a/demos/assets/textures/atlas.png and b/demos/assets/textures/atlas.png differ diff --git a/demos/compile_android.py b/demos/compile_android.py new file mode 100644 index 0000000..75d6106 --- /dev/null +++ b/demos/compile_android.py @@ -0,0 +1,91 @@ +import os +import ntpath +import sys + +def compile(sources, output): + files = [] + # r=root, d=directories, f = files + for path in sources: + for r, d, f in os.walk(path): + for file in f: + if ntpath.basename(file) != 'win_dll.d': + filename, file_extension = os.path.splitext(file) + if file_extension == '.d': + files.append(os.path.join(r, file)) + + ldc_path = 'ldc' + if 'LDC' in os.environ: + ldc_path = os.environ['LDC'] + + ldc_cmd = ldc_path + ' ' + ldc_flags + '-lib -mtriple=armv7-none-linux-androideabi -fvisibility=hidden -betterC -oq -od=obj/ --singleobj --of=' + output + ' ' + + for path in sources: + ldc_cmd += '-I' + path + ' ' + + for path in import_paths: + ldc_cmd += '-I' + path + ' ' + + for f in files: + ldc_cmd += f + ' ' + + print(ldc_cmd) + + if os.system(ldc_cmd): + print('some kind of error') + exit(0) + print() + +clean = 0 +ldc_flags = '--d-version=SDL_209 --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS ' +#import_paths = ['source','tests'] +import_paths = ['external/android','external/sources', 'external/imports', 'external/wasm_imports', '../source', 'utils/source', 'simple/source'] +build_tests = 0 + +for arg in sys.argv[1:]: + if(arg == '-release'): + ldc_flags += '-release ' + elif(arg == '-enable-inlining'): + ldc_flags += '-enable-inlining ' + elif(arg == '-O3'): + ldc_flags += '-O3 ' + elif(arg == '-O2'): + ldc_flags += '-O2 ' + elif(arg == '-O1'): + ldc_flags += '-O1 ' + elif(arg == '-O0'): + ldc_flags += '-O0 ' + elif(arg == '-Os'): + ldc_flags += '-Os ' + elif(arg == '-Oz'): + ldc_flags += '-Oz ' + elif(arg == '-g'): + ldc_flags += '-g ' + elif(arg == '-opt'): + ldc_flags += '-release -enable-inlining -O3 ' + else: + print('unknown argument: ' + arg) + exit() + +#compile(['source'], 'ecs.a') +compile(['external/wasm_imports/bindbc/sdl'], 'build/bindbc-sdl.a') +compile(['utils/source'], 'build/utils.a') +compile(['external/sources/mmutils'], 'build/mmutils.a') +compile(['external/sources/glad'], 'build/glad.a') +compile(['external/android/bindbc'], 'build/bindbc.a') +compile(['source'], 'build/demo.a') + +#compile(['external/wasm_imports/bindbc/sdl','utils/source','external/sources/mmutils','external/sources/glad'], 'build/asd.a') + +#export LDC_LIBS=/path/to/your/ldc-build-runtime.tmp/lib/ +CC = os.environ['NDK'] + '/toolchains/llvm/prebuilt/linux-x86_64/bin/clang' +TOOLCHAIN = os.environ['NDK'] + '/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64' +SYSROOT = os.environ['NDK'] + '/platforms/android-21/arch-arm' +#LDC_LIBS = os.environ['LDC_LIBS'] + '/libphobos2-ldc.a ' + os.environ['LDC_LIBS'] + '/libdruntime-ldc.a' +LDC_LIBS = '' +LIBS = '-L/platforms/android-21/arch-arm/usr/lib' + +os.system(CC + ' -Wl,-soname,libdemos.so -shared --sysroot=' + SYSROOT + ' ../obj/*.o obj/*.o ' + LDC_LIBS + ' -lgcc -gcc-toolchain ' + TOOLCHAIN + +' -no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi -fvisibility=hidden \ +-Wl,--gc-sections -Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro \ +-Wl,-z,now -mthumb -lm -lc -Llibs/armeabi-v7a -lcimgui -o libdemos.so') + diff --git a/demos/dub.json b/demos/dub.json index 1c1fe65..417b831 100644 --- a/demos/dub.json +++ b/demos/dub.json @@ -19,7 +19,8 @@ "libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"], "lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"], "dflags-ldc" : [ - "--ffast-math" + "--ffast-math", + "-enable-cross-module-inlining" ], "configurations" : [ { diff --git a/demos/external/android/bindbc/loader/package.d b/demos/external/android/bindbc/loader/package.d new file mode 100644 index 0000000..3ed4e31 --- /dev/null +++ b/demos/external/android/bindbc/loader/package.d @@ -0,0 +1,11 @@ + +// Copyright Michael D. Parker 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +module bindbc.loader; + +public +import bindbc.loader.sharedlib, + bindbc.loader.system; \ No newline at end of file diff --git a/demos/external/android/bindbc/loader/sharedlib.d b/demos/external/android/bindbc/loader/sharedlib.d new file mode 100644 index 0000000..ca3c654 --- /dev/null +++ b/demos/external/android/bindbc/loader/sharedlib.d @@ -0,0 +1,282 @@ + +// Copyright Michael D. Parker 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +module bindbc.loader.sharedlib; + +import core.stdc.stdlib; +import core.stdc.string; + +/// Handle to a shared library +struct SharedLib { + private void* _handle; +} + +/// Indicates an uninitialized or unassigned handle. +enum invalidHandle = SharedLib.init; + +// Contains information about shared library and symbol load failures. +struct ErrorInfo { +private: + char[32] _error; + char[96] _message; + +public @nogc nothrow @property: + /** + Returns the string "Missing Symbol" to indicate a symbol load failure, and + the name of a library to indicate a library load failure. + */ + const(char)* error() const { return _error.ptr; } + + /** + Returns a symbol name for symbol load failures, and a system-specific error + message for library load failures. + */ + const(char)* message() const { return _message.ptr; } +} + +private { + __gshared ErrorInfo[] _errors; + __gshared size_t _errorCount; +} + +@nogc nothrow: + +/** + Returns an slice containing all errors that have been accumulated by the + `load` and `bindSymbol` functions since the last call to `resetErrors`. +*/ +const(ErrorInfo)[] errors() +{ + return _errors[0 .. _errorCount]; +} + +/** + Returns the total number of errors that have been accumulated by the + `load` and `bindSymbol` functions since the last call to `resetErrors`. +*/ +size_t errorCount() +{ + return _errorCount; +} + +/** + Sets the error count to 0 and erases all accumulated errors. This function + does not release any memory allocated for the error list. +*/ +void resetErrors() +{ + _errorCount = 0; + memset(_errors.ptr, 0, _errors.length * ErrorInfo.sizeof); +} + +/* +void freeErrors() +{ + free(_errors.ptr); + _errors.length = _errorCount = 0; +} +*/ + +/** + Loads a symbol from a shared library and assigns it to a caller-supplied pointer. + + Params: + lib = a valid handle to a shared library loaded via the `load` function. + ptr = a pointer to a function or variable pointer whose declaration is + appropriate for the symbol being bound (it is up to the caller to + verify the types match). + symbolName = the name of the symbol to bind. +*/ +void bindSymbol(SharedLib lib, void** ptr, const(char)* symbolName) +{ + // Without this, DMD can hang in release builds + pragma(inline, false); + + assert(lib._handle); + auto sym = loadSymbol(lib._handle, symbolName); + if(sym) { + *ptr = sym; + } + else { + addErr("Missing Symbol", symbolName); + } +} + +/** + Formats a symbol using the Windows stdcall mangling if necessary before passing it on to + bindSymbol. + + Params: + lib = a valid handle to a shared library loaded via the `load` function. + ptr = a pointer to a function or variable pointer whose declaration is + appropriate for the symbol being bound (it is up to the caller to + verify the types match). + symbolName = the name of the symbol to bind. +*/ +void bindSymbol_stdcall(Func)(SharedLib lib, ref Func f, const(char)* symbolName) +{ + import bindbc.loader.system : bindWindows, bind32; + + static if(bindWindows && bind32) { + import core.stdc.stdio : snprintf; + import std.traits : ParameterTypeTuple; + + uint paramSize(A...)(A args) + { + size_t sum = 0; + foreach(arg; args) { + sum += arg.sizeof; + + // Align on 32-bit stack + if((sum & 3) != 0) { + sum += 4 - (sum & 3); + } + } + return sum; + } + + ParameterTypeTuple!f params; + char[128] mangled; + snprintf(mangled.ptr, mangled.length, "_%s@%d", symbolName, paramSize(params)); + symbolName = mangled.ptr; + } + bindSymbol(lib, cast(void**)&f, symbolName); +} + +/** + Loads a shared library from disk, using the system-specific API and search rules. + + libName = the name of the library to load. May include the full or relative + path for the file. +*/ +SharedLib load(const(char)* libName) +{ + auto handle = loadLib(libName); + if(handle) return SharedLib(handle); + else { + addErr(libName, null); + return invalidHandle; + } +} + +/** + Unloads a shared library from process memory. + + Generally, it is not necessary to call this function at program exit, as the system will ensure + any shared libraries loaded by the process will be unloaded then. However, any loaded shared + libraries that are no longer needed by the program during runtime, such as those that are part + of a "hot swap" mechanism, should be unloaded to free up resources. +*/ +void unload(ref SharedLib lib) { + if(lib._handle) { + unloadLib(lib._handle); + lib = invalidHandle; + } +} + +private: +void allocErrs() { + size_t newSize = _errorCount == 0 ? 16 : _errors.length * 2; + auto errs = cast(ErrorInfo*)malloc(ErrorInfo.sizeof * newSize); + if(!errs) exit(EXIT_FAILURE); + + if(_errorCount > 0) { + memcpy(errs, _errors.ptr, ErrorInfo.sizeof * _errors.length); + free(_errors.ptr); + } + + _errors = errs[0 .. newSize]; +} + +void addErr(const(char)* errstr, const(char)* message) +{ + if(_errors.length == 0 || _errorCount >= _errors.length) { + allocErrs(); + } + + auto pinfo = &_errors[_errorCount]; + strcpy(pinfo._error.ptr, errstr); + + if(message) { + strncpy(pinfo._message.ptr, message, pinfo._message.length); + pinfo._message[pinfo._message.length - 1] = 0; + } + else { + sysError(pinfo._message.ptr, pinfo._message.length); + } + ++_errorCount; +} + +version(Windows) +{ + import core.sys.windows.windows; + + void* loadLib(const(char)* name) + { + return LoadLibraryA(name); + } + + void unloadLib(void* lib) + { + FreeLibrary(lib); + } + + void* loadSymbol(void* lib, const(char)* symbolName) + { + return GetProcAddress(lib, symbolName); + } + + void sysError(char* buf, size_t len) + { + char* msgBuf; + enum uint langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + null, + GetLastError(), + langID, + cast(char*)&msgBuf, + 0, + null + ); + + if(msgBuf) { + strncpy(buf, msgBuf, len); + buf[len - 1] = 0; + LocalFree(msgBuf); + } + else strncpy(buf, "Unknown Error\0", len); + } +} +else version(Posix) { + import core.sys.posix.dlfcn; + + void* loadLib(const(char)* name) + { + return dlopen(name, RTLD_NOW); + } + + void unloadLib(void* lib) + { + dlclose(lib); + } + + void* loadSymbol(void* lib, const(char)* symbolName) + { + return dlsym(lib, symbolName); + } + + void sysError(char* buf, size_t len) + { + const (char)* msg = dlerror(); + strncpy(buf, msg != null ? msg : "Unknown Error", len); + buf[len - 1] = 0; + } +} +else static assert(0, "bindbc-loader is not implemented on this platform."); \ No newline at end of file diff --git a/demos/external/android/bindbc/loader/system.d b/demos/external/android/bindbc/loader/system.d new file mode 100644 index 0000000..c721100 --- /dev/null +++ b/demos/external/android/bindbc/loader/system.d @@ -0,0 +1,55 @@ + +// Copyright Michael D. Parker 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +module bindbc.loader.system; + +static if((void*).sizeof == 8) { + enum bind64 = true; + enum bind32 = false; +} +else { + enum bind64 = false; + enum bind32 = true; +} + +version(Windows) enum bindWindows = true; +else enum bindWindows = false; + +version(OSX) enum bindMac = true; +else enum bindMac = false; + +version(linux) enum bindLinux = true; +else enum bindLinux = false; + +version(Posix) enum bindPosix = true; +else enum bindPosix = false; + +version(Android) enum bindAndroid = true; +else enum bindAndroid = false; + +enum bindIOS = false; +enum bindWinRT = false; + +version(FreeBSD) { + enum bindBSD = true; + enum bindFreeBSD = true; + enum bindOpenBSD = false; +} +else version(OpenBSD) { + enum bindBSD = true; + enum bindFreeBSD = false; + enum bindOpenBSD = true; +} +else version(BSD) { + enum bindBSD = true; + enum bindFreeBSD = false; + enum bindOpenBSD = false; +} +else { + enum bindBSD = false; + enum bindFreeBSD = false; + enum bindOpenBSD = false; +} \ No newline at end of file diff --git a/demos/external/sources/cimgui/cimgui.d b/demos/external/sources/cimgui/cimgui.d index 507f616..1fc119f 100644 --- a/demos/external/sources/cimgui/cimgui.d +++ b/demos/external/sources/cimgui/cimgui.d @@ -2,9 +2,17 @@ //based on imgui.h file version "1.73" from Dear ImGui https://github.com/ocornut/imgui module cimgui.cimgui; -import core.stdc.stdarg; +// import core.stdc.stdarg; //import core.stdc.stdio; +version(WebAssembly) +{ + alias va_list = char*; + pragma(LDC_va_start) + void va_start(T)(out va_list ap, ref T parmn) @nogc; +} +else import core.stdc.stdarg; + extern (C): //alias ImU64 = ulong; diff --git a/demos/external/sources/glad/gl/loader.d b/demos/external/sources/glad/gl/loader.d index d640c61..8bc904f 100644 --- a/demos/external/sources/glad/gl/loader.d +++ b/demos/external/sources/glad/gl/loader.d @@ -35,14 +35,14 @@ bool open_gl() @nogc { return false; } else { version(OSX) { - enum const(char)*[] NAMES = [ + enum const(char)*[4] NAMES = [ "../Frameworks/OpenGL.framework/OpenGL", "/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" ]; } else { - enum const(char)*[] NAMES = ["libGL.so.1", "libGL.so"]; + enum const(char)*[2] NAMES = ["libGL.so.1", "libGL.so"]; } foreach(name; NAMES) { diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index 52136d4..bd05fe5 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -201,6 +201,21 @@ void instructionPause() static assert(0); } } + else version (Android) + { + version(LDC) + { + import ldc.attributes; + @optStrategy("none") + static void nop() + { + int i; + i++; + } + nop(); + } + else static assert(0); + } else version(WebAssembly) { version(LDC) @@ -381,8 +396,8 @@ version (MM_USE_POSIX_THREADS) void start(DG dg) { threadStart = dg; - int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - if(!ok)handle = pthread_t(); + int err = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); + if(err)handle = pthread_t(); //assert(ok == 0); } diff --git a/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d b/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d index 3ff5b4c..36bf76d 100644 --- a/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d +++ b/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d @@ -6,7 +6,11 @@ module bindbc.sdl.bind.sdllog; -import core.stdc.stdarg : va_list; +version(WebAssembly) +{ + alias va_list = char*; +} +else import core.stdc.stdarg : va_list; import bindbc.sdl.config; enum SDL_MAX_LOG_MESSAGE = 4096; diff --git a/demos/external/wasm_imports/bindbc/sdl/dynload.d b/demos/external/wasm_imports/bindbc/sdl/dynload.d index 0aa3b84..3a15c97 100644 --- a/demos/external/wasm_imports/bindbc/sdl/dynload.d +++ b/demos/external/wasm_imports/bindbc/sdl/dynload.d @@ -14,8 +14,8 @@ import bindbc.sdl.config, bindbc.sdl.bind; private { - SharedLib lib; - SDLSupport loadedVersion; + __gshared SharedLib lib; + __gshared SDLSupport loadedVersion; } void unloadSDL() @@ -664,14 +664,14 @@ SDLSupport loadSDL(const(char)* libName) lib.bindSymbol(cast(void**)&SDL_HasColorKey, "SDL_HasColorKey"); lib.bindSymbol(cast(void**)&SDL_GetDisplayOrientation, "SDL_GetDisplayOrientation"); - version(linux) { - lib.bindSymbol(cast(void**)&SDL_LinuxSetThreadPriority, "SDL_LinuxSetThreadPriority"); - } - else version(Android) { + version(Android) { lib.bindSymbol(cast(void**)&SDL_IsChromebook, "SDL_IsChromebook"); lib.bindSymbol(cast(void**)&SDL_IsDeXMode, "SDL_IsDeXMode"); lib.bindSymbol(cast(void**)&SDL_AndroidBackButton, "SDL_AndroidBackButton"); } + else version(linux) { + lib.bindSymbol(cast(void**)&SDL_LinuxSetThreadPriority, "SDL_LinuxSetThreadPriority"); + } if(errorCount() != errCount) return SDLSupport.badLibrary; else loadedVersion = SDLSupport.sdl209; diff --git a/demos/external/wasm_imports/bindbc/sdl/image.d b/demos/external/wasm_imports/bindbc/sdl/image.d index 8cf160e..8af6e81 100644 --- a/demos/external/wasm_imports/bindbc/sdl/image.d +++ b/demos/external/wasm_imports/bindbc/sdl/image.d @@ -229,8 +229,8 @@ else { } private { - SharedLib lib; - SDLImageSupport loadedVersion; + __gshared SharedLib lib; + __gshared SDLImageSupport loadedVersion; } void unloadSDLImage() diff --git a/demos/libs/armeabi-v7a/libcimgui.so b/demos/libs/armeabi-v7a/libcimgui.so new file mode 100755 index 0000000..5dca812 Binary files /dev/null and b/demos/libs/armeabi-v7a/libcimgui.so differ diff --git a/demos/source/app.d b/demos/source/app.d index ede93b5..5879daf 100644 --- a/demos/source/app.d +++ b/demos/source/app.d @@ -4,10 +4,11 @@ import bindbc.sdl; import cimgui.cimgui; +import game_core.basic; import game_core.job_updater; -import bubel.ecs.manager; import bubel.ecs.core; +import bubel.ecs.manager; import bubel.ecs.std; import ecs_utils.gfx.renderer; @@ -21,6 +22,7 @@ import glad.gl.gles2; import glad.gl.loader; import gui.manager; +import gui.tool_circle; extern (C) : @@ -39,6 +41,16 @@ struct Mouse bool left, right, middle; } +struct DemoCallbacks +{ + void function() register; + void function() initialize; + void function() deinitialize; + bool function() loop; + void function(SDL_Event*) event; + const (char)* tips; +} + struct Launcher { ECSJobUpdater* job_updater; @@ -47,10 +59,10 @@ struct Launcher SDL_Window* window; SDL_GLContext gl_context; EntityManager* manager; - bool function() loop; + /*bool function() loop; void function() end; - void function(SDL_Event*) event; - void function(vec2, Tool, int) tool; + void function(SDL_Event*) event;*/ + //void function(vec2, Tool, int, bool) tool; float scalling; ivec2 window_size = ivec2(1024,768); Renderer renderer; @@ -65,15 +77,20 @@ struct Launcher vec2 render_position; Tool used_tool; - int tool_size = 0; + int tool_size = 100; float tool_repeat = 0; float repeat_time = 0; + bool tool_show = true; + bool override_ = true; + bool tool_mode = true; + ToolCircle* tool_circle; + bool show_filtered; bool swap_interval = true; float windows_alpha = 0.75; - const (char)* tips; + //const (char)* tips; bool show_stat_wnd = true; bool show_tips = true; @@ -93,17 +110,19 @@ struct Launcher float draw_time = 0; } - void switchDemo(void function() start, bool function() loop, void function() end, void function(SDL_Event*) event, void function(vec2, Tool, int) tool, const (char)* tips) + DemoCallbacks demo; + + void switchDemo(DemoCallbacks callbacks)//void function() start, bool function() loop, void function() end, void function(SDL_Event*) event, const (char)* tips) { gui_manager.clear(); //launcher.ent - if(this.end)this.end(); - manager.begin(); manager.update("clean"); manager.end(); + if(this.demo.deinitialize)this.demo.deinitialize(); + foreach(ref system; manager.systems) { if(system.id != CountSystem.system_id && system.id != CleanSystem.system_id)system.disable(); @@ -112,12 +131,154 @@ struct Launcher /*launcher.manager.getSystem(CountSystem.system_id).enable(); launcher.manager.getSystem(CleanSystem.system_id).enable();//*/ - if(start)start(); - this.loop = loop; + if(callbacks.register)callbacks.register(); + if(callbacks.initialize)callbacks.initialize(); + demo = callbacks; + /*this.loop = loop; this.end = end; this.event = event; - this.tips = tips; - this.tool = tool; + this.tips = tips;*/ + //this.tool = tool; + } + + bool filterEntity(ref const Entity entity) + { + EntityMeta meta = entity.getMeta(); + foreach(id;gui_manager.filter_list) + { + if(!meta.hasComponent(id))return false; + } + if(used_tool == Tool.component_manipulator) + { + if(!meta.hasComponent(gui_manager.getSelectedComponent().component_id))return false; + } + return true; + } + + void processTool(vec2 position, bool mode) + { + static struct Iterator + { + float size2; + vec2 position; + ComponentRef[] add_comps; + ushort[] rem_comps; + ushort[] filter; + + bool filterEntity(ref const Entity entity) + { + EntityMeta meta = entity.getMeta(); + foreach(id;filter) + { + if(!meta.hasComponent(id))return false; + } + return true; + } + + void removeEntity(IteratorSystem.EntitiesData data) + { + if(!filterEntity(data.entity[0]))return; + foreach(i;0..data.length) + { + vec2 rel_vec = data.location[i] - position; + float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; + if(length < size2)gEM.removeEntity(data.entity[i].id); + } + } + + void addComponent(IteratorSystem.EntitiesData data) + { + if(!filterEntity(data.entity[0]))return; + foreach(i;0..data.length) + { + vec2 rel_vec = data.location[i] - position; + float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; + if(length < size2)gEM.addComponents(data.entity[i].id, add_comps); + } + } + + void overrideComponent(IteratorSystem.EntitiesData data) + { + if(!filterEntity(data.entity[0]))return; + foreach(i;0..data.length) + { + vec2 rel_vec = data.location[i] - position; + float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; + if(length < size2) + { + gEM.removeComponents(data.entity[i].id, rem_comps); + gEM.addComponents(data.entity[i].id, add_comps); + } + } + } + + void removeComponent(IteratorSystem.EntitiesData data) + { + if(!filterEntity(data.entity[0]))return; + foreach(i;0..data.length) + { + vec2 rel_vec = data.location[i] - position; + float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; + if(length < size2)gEM.removeComponents(data.entity[i].id, rem_comps); + } + } + } + + float half_size = tool_size * 0.5; + float size2 = half_size * half_size; + Iterator iterator; + iterator.size2 = size2; + iterator.position = position; + iterator.filter = gui_manager.filter_list[]; + + switch(used_tool) + { + case Tool.entity_spawner: + if(mode) + { + if(gui_manager.templates.length == 0)return; + EntityTemplate* tmpl = gui_manager.getSelectedTemplate(); + CLocation* location = tmpl.getComponent!CLocation; + if(location) + { + position += randomCircularSample() * half_size; + //if(position.y < 16)position.y = 16; + //else if(position.y > 299)position.y = 299; + *location = position; + } + manager.addEntity(tmpl); + } + else + { + manager.callEntitiesFunction!IteratorSystem(&iterator.removeEntity); + } + break; + case Tool.component_manipulator: + { + if(gui_manager.components.length == 0)return; + if(mode) + { + ComponentRef[1] comps = [gui_manager.getSelectedComponent()]; + iterator.add_comps = comps; + if(launcher.override_) + { + ushort[1] rcomps = [gui_manager.getSelectedComponent().component_id]; + iterator.rem_comps = rcomps; + manager.callEntitiesFunction!IteratorSystem(&iterator.overrideComponent); + } + else manager.callEntitiesFunction!IteratorSystem(&iterator.addComponent); + } + else + { + ushort[1] comps = [gui_manager.getSelectedComponent().component_id]; + iterator.rem_comps = comps; + manager.callEntitiesFunction!IteratorSystem(&iterator.removeComponent); + } + } + break; + default: + break; + } } bool getKeyState(SDL_Scancode key) @@ -184,6 +345,28 @@ struct CleanSystem } } +struct IteratorSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + CLocation[] location; + } + + bool onBegin() + { + return false; + } + + void onUpdate(EntitiesData) + { + + } +} + void mainLoop(void* arg) { __gshared double time = 0; @@ -203,18 +386,43 @@ void mainLoop(void* arg) temp_fps = 0; } - SDL_Event event; while (SDL_PollEvent(&event)) { - version(WebAssembly)ImGui_ImplSDL2_ProcessEvent(&event); - else ImGui_ImplSDL2_ProcessEvent(&event); - if(launcher.event)launcher.event(&event); + ImGui_ImplSDL2_ProcessEvent(&event); + if(launcher.demo.event)launcher.demo.event(&event); if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)) { quit(); *cast(bool*)arg = false; return; } + else if(event.type == SDL_KEYDOWN) + { + if(event.key.state) + { + if(SDL_GetModState() & KMOD_CTRL) + { + switch(event.key.keysym.scancode) + { + case SDL_SCANCODE_1:launcher.used_tool=Tool.entity_spawner;break; + case SDL_SCANCODE_2:launcher.used_tool=Tool.component_manipulator;break; + case SDL_SCANCODE_3:launcher.used_tool=Tool.selector;break; + default:break; + } + } + else + { + switch(event.key.keysym.scancode) + { + case SDL_SCANCODE_1:break; + case SDL_SCANCODE_2:break; + case SDL_SCANCODE_3:break; + case SDL_SCANCODE_4:break; + default:break; + } + } + } + } else if(event.type == SDL_WINDOWEVENT) { switch(event.window.event) @@ -239,9 +447,12 @@ void mainLoop(void* arg) case SDL_BUTTON_MIDDLE:launcher.mouse.middle = true;break; default:break; } - if(launcher.tool && event.button.button == SDL_BUTTON_LEFT && launcher.tool_repeat == 0 && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow)) + if(!igIsAnyItemHovered())igSetWindowFocus(); + if(!igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow)) { - launcher.tool(vec2(event.button.x, launcher.window_size.y - event.button.y) * launcher.scalling - launcher.render_position, launcher.used_tool, launcher.tool_size); + launcher.repeat_time = 0; + if(event.button.button == SDL_BUTTON_LEFT)launcher.processTool(vec2(event.button.x, launcher.window_size.y - event.button.y) * launcher.scalling - launcher.render_position,launcher.tool_mode); + else if(event.button.button == SDL_BUTTON_RIGHT)launcher.processTool(vec2(event.button.x, launcher.window_size.y - event.button.y) * launcher.scalling - launcher.render_position,!launcher.tool_mode); } } else if(event.type == SDL_MOUSEBUTTONUP) @@ -257,17 +468,67 @@ void mainLoop(void* arg) else if(event.type == SDL_MOUSEMOTION) { launcher.mouse.position = vec2(event.motion.x, launcher.window_size.y - event.motion.y); + }else if(event.type == SDL_MOUSEWHEEL) + { + if(!igIsAnyItemHovered() && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow)) + { + if(SDL_GetModState() & KMOD_CTRL) + { + float sign = 1; + if(event.wheel.y < 0)sign = -1; + float val = /*sign * event.wheel.y */ launcher.tool_repeat * 0.25; + if(val < 0.1)val = 0.1; + launcher.tool_repeat -= sign * val; + if(launcher.tool_repeat < 0)launcher.tool_repeat = 0; + else if(launcher.tool_repeat > 1000)launcher.tool_repeat = 1000; + } + else if(SDL_GetModState() & KMOD_SHIFT) + { + int sign = 1; + if(event.wheel.y < 0)sign = -1; + switch(launcher.used_tool) + { + case Tool.entity_spawner: + launcher.gui_manager.selectTemplate(launcher.gui_manager.selected_template-sign); + break; + case Tool.component_manipulator: + launcher.gui_manager.selectComponent(launcher.gui_manager.selected_component-sign); + break; + default:break; + } + } + else + { + int sign = 1; + if(event.wheel.y < 0)sign = -1; + int val = /*sign * event.wheel.y */ launcher.tool_size / 4; + if(val < 1)val = 1; + launcher.tool_size -= sign * val; + if(launcher.tool_size < 1)launcher.tool_size = 1; + else if(launcher.tool_size > 256)launcher.tool_size = 256; + } + } } } - if(launcher.tool && launcher.tool_repeat != 0 && launcher.mouse.left && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow)) + if(launcher.tool_repeat != 0 && (launcher.mouse.left || launcher.mouse.right) && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow)) { + bool mode = launcher.tool_mode; + if(launcher.mouse.right)mode = !mode; float range = 500.0 / cast(float)launcher.tool_repeat; launcher.repeat_time += launcher.delta_time; - while(launcher.repeat_time > range) + if(launcher.used_tool != Tool.entity_spawner || !mode) { - launcher.repeat_time -= range; - launcher.tool((launcher.mouse.position*launcher.scalling)-launcher.render_position, launcher.used_tool, launcher.tool_size); + if(launcher.repeat_time > range)launcher.processTool((launcher.mouse.position*launcher.scalling)-launcher.render_position, mode); + while(launcher.repeat_time > range)launcher.repeat_time -= range; + } + else + { + while(launcher.repeat_time > range) + { + launcher.repeat_time -= range; + launcher.processTool((launcher.mouse.position*launcher.scalling)-launcher.render_position, mode); + } } } @@ -278,7 +539,8 @@ void mainLoop(void* arg) } else { - ImGuiImplOpenGL2NewFrame(); + //ImGuiImplOpenGL2NewFrame(); + ImGui_ImplOpenGL3_NewFrame(); ImGuiImplSDL2NewFrame(launcher.window); } @@ -301,17 +563,32 @@ void mainLoop(void* arg) if(igMenuItemBool("Simpe",null,false,true)) { import demos.simple; - launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips); + launcher.switchDemo(getSimpleDemo());//&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,Simple.tips); } if(igMenuItemBool("Snake",null,false,true)) { import demos.snake; - launcher.switchDemo(&snakeStart,&snakeLoop,&snakeEnd,&snakeEvent,&snakeTool,Snake.tips); + launcher.switchDemo(getSnakeDemo());//&snakeStart,&snakeLoop,&snakeEnd,&snakeEvent,Snake.tips); } - if(igMenuItemBool("Space invaders",null,false,true)) + if(igMenuItemBool("Space Invaders",null,false,true)) { import demos.space_invaders; - launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,&spaceInvadersTool,SpaceInvaders.tips); + launcher.switchDemo(getSpaceInvadersDemo());//&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,SpaceInvaders.tips); + } + if(igMenuItemBool("Particles",null,false,true)) + { + import demos.particles; + launcher.switchDemo(getParticlesDemo());//&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); + } + if(igMenuItemBool("Brick Breaker",null,false,true)) + { + import demos.brick_breaker; + launcher.switchDemo(getBrickBreakerDemo());//&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); + } + if(igMenuItemBool("Sandbox",null,false,true)) + { + import demos.sandbox; + launcher.switchDemo(getSanboxDemo());//&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); } igEndMenu(); } @@ -344,22 +621,11 @@ void mainLoop(void* arg) } if(igBeginMenu("Show",true)) { - if(igMenuItemBool("Statistics",null,launcher.show_stat_wnd,true)) - { - launcher.show_stat_wnd = !launcher.show_stat_wnd; - } - else if(igMenuItemBool("Demo",null,launcher.show_demo_wnd,true)) - { - launcher.show_demo_wnd = !launcher.show_demo_wnd; - } - else if(igMenuItemBool("Tips",null,launcher.show_tips,true)) - { - launcher.show_tips = !launcher.show_tips; - } - else if(igMenuItemBool("Virual keys",null,launcher.show_virtual_keys_wnd,true)) - { - launcher.show_virtual_keys_wnd = !launcher.show_virtual_keys_wnd; - } + igMenuItemBoolPtr("Statistics",null,&launcher.show_stat_wnd,true); + igMenuItemBoolPtr("Demo",null,&launcher.show_demo_wnd,true); + igMenuItemBoolPtr("Tips",null,&launcher.show_tips,true); + igMenuItemBoolPtr("Virual keys",null,&launcher.show_virtual_keys_wnd,true); + igMenuItemBoolPtr("Profile",null,&launcher.show_profile_wnd,true); igEndMenu(); } if(igBeginMenu("Style",true)) @@ -456,7 +722,7 @@ void mainLoop(void* arg) igSetNextWindowBgAlpha(launcher.windows_alpha); if(igBegin("Tips",&launcher.show_tips,ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { - igTextWrapped(launcher.tips); + igTextWrapped(launcher.demo.tips); } igEnd(); } @@ -464,20 +730,22 @@ void mainLoop(void* arg) if(launcher.show_demo_wnd) { igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, 30), ImGuiCond_Once, ImVec2(0,0)); - igSetNextWindowSize(ImVec2(250, 500), ImGuiCond_Once); + igSetNextWindowSize(ImVec2(250, launcher.window_size.y - 60), ImGuiCond_Once); if(igBegin("Demo",&launcher.show_demo_wnd,0)) { ImDrawList* draw_list = igGetWindowDrawList(); - //igBeginGroup(); + igBeginGroup(); launcher.gui_manager.gui(); - //igEndGroup(); + igEndGroup(); + ImDrawList_AddRect(draw_list, igGetItemRectMin(), ImVec2(igGetWindowPos().x+igGetWindowWidth()-2,igGetItemRectMax().y), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 4, ImDrawCornerFlags_All, 1); + //ImDrawList_AddRect(draw_list, igGetItemRectMin(), igGetItemRectMax(), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), -1, 0, 1); //igBeginChildFrame(1,ImVec2(0,-1),ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_ChildWindow); //igBeginChild("Tool frame",ImVec2(-1,-1),true,0); - if(igCollapsingHeader("Tool##ToolHeader", ImGuiTreeNodeFlags_SpanAvailWidth)) + igBeginGroup(); + if(igCollapsingHeader("Tool##ToolHeader", ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) { igIndent(8); - igBeginGroup(); if(igBeginCombo("Tool",tool_strings[launcher.used_tool],0)) { if(igSelectable("Entity spawner",false,0,ImVec2(0,0))) @@ -494,14 +762,44 @@ void mainLoop(void* arg) } igEndCombo(); } + if(igIsItemHovered(0))igSetTooltip("Select tool (CTRL + 1,2,3)"); + igCheckbox("Show Tool", &launcher.tool_show); + if(igIsItemHovered(0))igSetTooltip("Show/hide graphical tool representation"); + igSameLine(0,4); + igCheckbox("Show Filtered", &launcher.show_filtered); + if(igIsItemHovered(0))igSetTooltip("Show/hide filtered entities"); + if(launcher.used_tool == Tool.component_manipulator) + { + igCheckbox("Override", &launcher.override_); + } + //igSelectable("Selectabe",false,ImGuiSelectableFlags_None,ImVec2(0,0)); + if(launcher.used_tool != Tool.selector) + { + if(igRadioButtonBool("Add", launcher.tool_mode))launcher.tool_mode = true; + if(igIsItemHovered(0))igSetTooltip("Tool should adding (Entities or components)"); + igSameLine(0,4); + if(igRadioButtonBool("Remove", !launcher.tool_mode))launcher.tool_mode = false; + if(igIsItemHovered(0))igSetTooltip("Tool should removing (Entities or components)"); + } + igSliderInt("Tool size", &launcher.tool_size, 0, 256, null); igSliderFloat("Tool repeat", &launcher.tool_repeat, 0, 1024, null, 4); launcher.gui_manager.toolGui(); - igEndGroup(); - ImDrawList_AddRect(draw_list, igGetItemRectMin(), igGetItemRectMax(), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 4, ImDrawCornerFlags_All, 1); igUnindent(8); } + igEndGroup(); + ImDrawList_AddRect(draw_list, igGetItemRectMin(), ImVec2(igGetWindowPos().x+igGetWindowWidth()-2,igGetItemRectMax().y), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 2, ImDrawCornerFlags_All, 1); + + //igBeginGroup(); + if(igCollapsingHeader("Filter", ImGuiTreeNodeFlags_SpanAvailWidth)) + { + igIndent(8); + launcher.gui_manager.filterGUI(); + igUnindent(8); + } + //igEndGroup(); + //ImDrawList_AddRect(draw_list, igGetItemRectMin(), ImVec2(igGetWindowPos().x+igGetWindowWidth()-2,igGetItemRectMax().y), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 2, ImDrawCornerFlags_All, 1); //igEndChild(); //igEndChildFrame(); @@ -517,7 +815,8 @@ void mainLoop(void* arg) if(launcher.show_profile_wnd) { - igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, launcher.window_size.y - 280), ImGuiCond_Once, ImVec2(0,0)); + //igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, launcher.window_size.y - 280), ImGuiCond_Once, ImVec2(0,0)); + igSetNextWindowPos(ImVec2(8, launcher.window_size.y - 258), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowSize(ImVec2(250, 250), ImGuiCond_Once); if(igBegin("Profile",&launcher.show_profile_wnd,0)) { @@ -565,7 +864,7 @@ void mainLoop(void* arg) double loop_time = launcher.getTime(); launcher.job_updater.pool.tryWaitCount = 10000; - if(launcher.loop && !launcher.loop()) + if(launcher.demo.loop && !launcher.demo.loop()) { quit(); *cast(bool*)arg = false; @@ -578,6 +877,8 @@ void mainLoop(void* arg) launcher.renderer.present(); draw_time = launcher.getTime() - draw_time; + if(launcher.tool_show)launcher.tool_circle.draw(&launcher.renderer, (launcher.mouse.position*launcher.scalling)-launcher.render_position, cast(float)launcher.tool_size, launcher.renderer.view_size.y*6*launcher.scalling); + __gshared float plot_time = 0; __gshared uint plot_samples = 0; plot_time += launcher.delta_time; @@ -632,7 +933,9 @@ void mainLoop(void* arg) igRender(); version(WebAssembly)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); - else ImGuiImplOpenGL2RenderDrawData(igGetDrawData()); + else version(Android)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); + else ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); + //ImGuiImplOpenGL2RenderDrawData(igGetDrawData()); //launcher.renderer.clear(); //launcher.renderer.present(); @@ -643,24 +946,64 @@ void mainLoop(void* arg) void quit() { + import game_core.rendering : TexCoordsManager; launcher.gui_manager.clear(); Mallocator.dispose(launcher.gui_manager); + if(launcher.demo.deinitialize)launcher.demo.deinitialize(); + launcher.manager.destroy(); launcher.manager = null; + TexCoordsManager.destroy(); + + SDL_Quit(); + version(WebAssembly)emscripten_cancel_main_loop(); } -int main(int argc, char** argv) +version(Android) { + export extern (C) int SDL_main(int argc, char** args) + { + return app_main(argc,args); + } + + import ldc.attributes; + + extern (C) __gshared + { + @section(".tdata") + int _tlsstart = 0; + @section(".tcommon") + int _tlsend = 0; + } + +} +else +{ + extern (C) int main(int argc, char** argv) + { + return app_main(argc,argv); + } +} + +int app_main(int argc, char** argv) +//int main(int argc, char** argv) +{ + + version(BindSDL_Static){} + else + { + loadSDL(); + loadSDLImage(); + } if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL could not initialize! SDL_Error: %s", SDL_GetError()); return -1; } - SDL_version sdl_version; SDL_GetVersion(&sdl_version); printf("SDL version: %u.%u.%u\n",cast(uint)sdl_version.major,cast(uint)sdl_version.minor,cast(uint)sdl_version.patch); @@ -669,6 +1012,7 @@ int main(int argc, char** argv) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); launcher.window = SDL_CreateWindow("Simple", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, launcher.window_size.x, launcher.window_size.y, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + SDL_MaximizeWindow(launcher.window); launcher.gl_context = SDL_GL_CreateContext(launcher.window); launcher.context = igCreateContext(null); @@ -692,6 +1036,21 @@ int main(int argc, char** argv) return -3; } } + else version(Android) + { + //gladLoadGL(); + gladLoadGLES2(x => SDL_GL_GetProcAddress(x)); + if(!ImGuiImplSDL2InitForOpenGL(launcher.window,launcher.gl_context)) + { + printf("ImGui initialization failed!"); + return -2; + } + if(!ImGui_ImplOpenGL3_Init("#version 100")) + { + printf("ImGui OpenGL initialization failed!"); + return -3; + } + } else { gladLoadGL(); @@ -700,17 +1059,19 @@ int main(int argc, char** argv) printf("ImGui initialization failed!"); return -2; } - if(!ImGuiImplOpenGL2Init()) + //if(!ImGuiImplOpenGL2Init()) + if(!ImGui_ImplOpenGL3_Init("#version 120")) { printf("ImGui OpenGL initialization failed!"); return -3; } } - - ImFontConfig* config = ImFontConfig_ImFontConfig(); + + //ImFontConfig* config = ImFontConfig_ImFontConfig(); ImGuiIO* io = igGetIO(); const ushort* font_ranges = ImFontAtlas_GetGlyphRangesDefault(io.Fonts); - ImFontAtlas_AddFontFromFileTTF(io.Fonts,"assets/fonts/Ruda-Bold.ttf", 15.0f, config, font_ranges); + ImFontAtlas_AddFontFromFileTTF(io.Fonts,"assets/fonts/Ruda-Bold.ttf", 15.0f, null, font_ranges); + //ImFontConfig_destroy(config); setStyle(3); @@ -728,14 +1089,19 @@ int main(int argc, char** argv) launcher.manager.registerPass("clean"); + launcher.manager.registerComponent!CLocation; + launcher.manager.registerSystem!CountSystem(10000); launcher.manager.registerSystem!CleanSystem(0,"clean"); + launcher.manager.registerSystem!IteratorSystem(0,"clean"); launcher.manager.endRegister(); loadGFX(); launcher.renderer.initialize(); + import game_core.rendering : TexCoordsManager; + TexCoordsManager.initialize(); import mmutils.thread_pool : ThreadPool; launcher.threads = ThreadPool.getCPUCoresCount(); @@ -746,8 +1112,14 @@ int main(int argc, char** argv) { import demos.simple; import demos.space_invaders; - // launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,&spaceInvadersTool,SpaceInvaders.tips); - launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips); + import demos.particles; + import demos.brick_breaker; + // launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,SpaceInvaders.tips); + // launcher.switchDemo(&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); + // launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,Simple.tips); + // launcher.switchDemo(getParticlesDemo()); + // launcher.switchDemo(getSimpleDemo()); + launcher.switchDemo(getBrickBreakerDemo()); } int key_num; @@ -775,8 +1147,6 @@ int main(int argc, char** argv) } } - EntityManager.destroy(); - return 0; } @@ -792,7 +1162,7 @@ void loadGFX() Texture.__loadBackend(); Renderer.__loadBackend(); - GfxConfig.materials = Mallocator.makeArray!Material(1); + GfxConfig.materials = Mallocator.makeArray!Material(3); GfxConfig.meshes = Mallocator.makeArray!Mesh(1); float[16] vertices = [-0.5,-0.5, 0,1, -0.5,0.5, 0,0, 0.5,-0.5, 1,1, 0.5,0.5, 1,0]; @@ -839,7 +1209,93 @@ void loadGFX() GfxConfig.materials[0].data.bindings[0] = GfxConfig.materials[0].getLocation("tex"); + Shader vsh2; + vsh2.create(); + vsh2.load("assets/shaders/circle.vp"); + vsh2.compile(); + + Shader fsh2; + fsh2.create(); + fsh2.load("assets/shaders/circle.fp"); + fsh2.compile(); + + GfxConfig.materials[1].create(); + GfxConfig.materials[1].data.blend_mode = Material.BlendMode.mixed; + GfxConfig.materials[1].data.mode = Material.TransformMode.position; + Material.ShaderModule[1] modules2 = [Material.ShaderModule(vsh2,fsh2)]; + GfxConfig.materials[1].attachModules(modules2); + //GfxConfig.materials[0]. + //GfxConfig.materials[0].load(load_data.materials[i].str); + GfxConfig.materials[1].compile(); + GfxConfig.materials[1].bindAttribLocation("positions",0); + //GfxConfig.materials[1].bindAttribLocation("tex_coords",1); + //GfxConfig.materials[1].bindAttribLocation("depth",2); + //GfxConfig.materials[1].bindAttribLocation("vcolor",3); + GfxConfig.materials[1].link(); + + /* import std.stdio; + writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions")); + writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords")); + writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth")); + writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/ + + GfxConfig.materials[1].data.uniforms = Mallocator.makeArray!(Material.Uniform)(2); + GfxConfig.materials[1].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[1].getLocation("matrix_1"), 0); + GfxConfig.materials[1].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[1].getLocation("matrix_2"), 16); + //GfxConfig.materials[1].data.uniforms[2] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("uv_transform"), 32); + //GfxConfig.materials[1].data.bindings = Mallocator.makeArray!(int)(1); + //GfxConfig.materials[1].data.bindings[0] = GfxConfig.materials[0].getLocation("tex"); + + + + + + Shader vsh3; + vsh3.create(); + vsh3.load("assets/shaders/additive_particles.vp"); + vsh3.compile(); + + Shader fsh3; + fsh3.create(); + fsh3.load("assets/shaders/additive_particles.fp"); + fsh3.compile(); + + GfxConfig.materials[2].create(); + GfxConfig.materials[2].data.blend_mode = Material.BlendMode.opaque; + GfxConfig.materials[2].data.mode = Material.TransformMode.position; + Material.ShaderModule[1] modules3 = [Material.ShaderModule(vsh3,fsh3)]; + GfxConfig.materials[2].attachModules(modules3); + //GfxConfig.materials[0]. + //GfxConfig.materials[0].load(load_data.materials[i].str); + GfxConfig.materials[2].compile(); + GfxConfig.materials[2].bindAttribLocation("positions",0); + GfxConfig.materials[2].bindAttribLocation("tex_coords",1); + GfxConfig.materials[2].bindAttribLocation("depth",2); + GfxConfig.materials[2].bindAttribLocation("vcolor",3); + GfxConfig.materials[2].link(); + + /* import std.stdio; + writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions")); + writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords")); + writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth")); + writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/ + + GfxConfig.materials[2].data.uniforms = Mallocator.makeArray!(Material.Uniform)(3); + GfxConfig.materials[2].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0); + GfxConfig.materials[2].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_2"), 16); + GfxConfig.materials[2].data.uniforms[2] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("uv_transform"), 32); + GfxConfig.materials[2].data.bindings = Mallocator.makeArray!(int)(1); + GfxConfig.materials[2].data.bindings[0] = GfxConfig.materials[0].getLocation("tex"); + GfxConfig.materials[2].data.blend_mode = Material.BlendMode.additive; + + + + + + /*glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/ + + launcher.tool_circle = Mallocator.make!ToolCircle; } \ No newline at end of file diff --git a/demos/source/demos/brick_breaker.d b/demos/source/demos/brick_breaker.d new file mode 100644 index 0000000..a170076 --- /dev/null +++ b/demos/source/demos/brick_breaker.d @@ -0,0 +1,501 @@ +module demos.brick_breaker; + +import app; + +import bindbc.sdl; + +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; + +import cimgui.cimgui; + +import ecs_utils.gfx.texture; +import ecs_utils.math.vector; +import ecs_utils.utils; + +import game_core.basic; +import game_core.rendering; +import game_core.collision; + +extern(C): + +private enum float px = 1.0/512.0; + +/*####################################################################################################################### +------------------------------------------------ Components ------------------------------------------------------------------ +#######################################################################################################################*/ + +/*struct CLocation +{ + mixin ECS.Component; + + alias location this; + + vec2 location; +}*/ + +struct CBrick +{ + mixin ECS.Component; +} + +struct CPaddle +{ + mixin ECS.Component; +} + +struct CBall +{ + mixin ECS.Component; + + ubyte radius; +} + +struct CHitPoints +{ + mixin ECS.Component; + + alias value this; + + short value; +} + +// struct CVelocityFactor +// { +// mixin ECS.Component; + +// alias value this; + +// vec2 value = vec2(1); +// } + +// struct CVelocity +// { +// mixin ECS.Component; + +// alias value this; + +// vec2 value = vec2(0); +// } + +struct EDamage +{ + mixin ECS.Event; + + ubyte damage = 1; +} + +/*####################################################################################################################### +------------------------------------------------ Systems ------------------------------------------------------------------ +#######################################################################################################################*/ + +// struct MoveSystem +// { +// mixin ECS.System!64; + +// struct EntitiesData +// { +// uint length; +// CLocation[] location; +// @readonly CVelocity[] velocity; +// @optional @readonly CVelocityFactor[] vel_factor; +// } + +// void onUpdate(EntitiesData data) +// { +// if(data.vel_factor) +// { +// foreach(i; 0..data.length) +// { +// data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.delta_time; +// } +// } +// else +// { +// foreach(i; 0..data.length) +// { +// data.location[i] += data.velocity[i] * launcher.delta_time; +// } +// } +// } +// } + +struct EdgeCollisionSystem +{ + mixin ECS.System!64; + + struct EntitiesData + { + uint length; + CLocation[] location; + CVelocity[] velocity; + //CBall[] ball_flag; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + if(data.location[i].x < 0) + { + if(data.velocity[i].x < 0)data.velocity[i].x = -data.velocity[i].x; + data.location[i].x = 0; + } + else if(data.location[i].x > 400) + { + if(data.velocity[i].x > 0)data.velocity[i].x = -data.velocity[i].x; + data.location[i].x = 400; + } + + if(data.location[i].y < 0) + { + if(data.velocity[i].y < 0)data.velocity[i].y = -data.velocity[i].y; + data.location[i].y = 0; + } + else if(data.location[i].y > 300) + { + if(data.velocity[i].y > 0)data.velocity[i].y = -data.velocity[i].y; + data.location[i].y = 300; + } + } + } +} + +struct BallCollisionSystem +{ + mixin ECS.System!64; + + mixin ECS.ReadOnlyDependencies!(ShootGridDependency, BVHDependency); + + struct EntitiesData + { + ///variable named "length" contain entites count + uint length; + const (Entity)[] entity; + CVelocity[] velocity; + @readonly CLocation[] location; + @readonly CScale[] scale; + @readonly CBall[] ball_flag; + } + + struct State + { + bool test(EntityID id) + { + Entity* entity = launcher.manager.getEntity(id); + if(entity) + { + CLocation* location = entity.getComponent!CLocation; + CScale* scale = entity.getComponent!CScale; + if(location && scale) + { + float radius = data.scale[i].x; + vec2 rel_pos = *location - data.location[i]; + vec2 abs_rel_pos = rel_pos; + if(abs_rel_pos.x < 0)abs_rel_pos.x = -abs_rel_pos.x; + if(abs_rel_pos.y < 0)abs_rel_pos.y = -abs_rel_pos.y; + + vec2 half_scale = *scale * 0.25f; + + if(abs_rel_pos.x < half_scale.x + radius && + abs_rel_pos.y < half_scale.y + radius) + { + if(abs_rel_pos.x < half_scale.x) + { + if(rel_pos.y * data.velocity[i].y > 0) + { + data.velocity[i].y = -data.velocity[i].y; + launcher.manager.sendEvent(id,EDamage(1)); + return false; + } + } + else if(abs_rel_pos.y < half_scale.y) + { + if(rel_pos.x * data.velocity[i].x > 0) + { + data.velocity[i].x = -data.velocity[i].x; + launcher.manager.sendEvent(id,EDamage(1)); + return false; + } + } + else + { + vec2 vector = abs_rel_pos - half_scale; + if(rel_pos.x > 0)vector.x = -vector.x; + if(rel_pos.y > 0)vector.y = -vector.y; + + float pow_dist = vector.length2(); + if(pow_dist < radius*radius) + { + vector = vector / sqrtf(pow_dist); + data.velocity[i] = data.velocity[i] - vector * (2 * dot(vector, data.velocity[i])); + launcher.manager.sendEvent(id,EDamage(1)); + return false; + } + } + } + } + } + return true; + } + + EntitiesData data; + uint i; + } + + ShootGrid* grid; + BVHTree* tree; + BVHTree* static_tree; + + bool onBegin() + { + //grid = launcher.manager.getSystem!ShootGridManager().grid; + tree = launcher.manager.getSystem!BVHBuilder().tree; + static_tree = launcher.manager.getSystem!StaticBVHBuilder().tree; + //if(grid is null)return false; + if(tree is null || static_tree is null)return false; + else return true; + } + + void onUpdate(EntitiesData data) + { + // State state; + // state.data = data; + // EntityID id; + // foreach(i; 0..data.length) + // { + // state.i = i; + // float radius = data.scale[i].x; + // if(grid.test(id, data.location[i] - radius, data.location[i] + radius, ubyte.max)) + // { + // state.test(id); + // } + // } + State state; + state.data = data; + foreach(i; 0..data.length) + { + state.i = i; + //float radius = data.scale[i].x; + AABB bounding = AABB(data.location[i]-data.scale[i], data.location[i]+data.scale[i]); + tree.test(bounding, cast(bool delegate(EntityID id))&state.test); + static_tree.test(bounding, cast(bool delegate(EntityID id))&state.test); + } + } +} + +struct DamageSystem +{ + mixin ECS.System!64; + + mixin ECS.ReadOnlyDependencies!(ShootGridDependency); + + struct EntitiesData + { + ///variable named "length" contain entites count + uint length; + const (Entity)[] entity; + CHitPoints[] hit_points; + } + + void handleEvent(Entity* entity, EDamage event) + { + EntityMeta meta = entity.getMeta(); + CHitPoints* hp = meta.getComponent!CHitPoints; + hp.value -= event.damage; + if(hp.value < 0)launcher.manager.removeEntity(entity.id); + } + +} + +/*####################################################################################################################### +------------------------------------------------ Functions ------------------------------------------------------------------ +#######################################################################################################################*/ + +struct BrickBreakerDemo +{ + __gshared const (char)* tips = "Brick breaker demo. It's a game about destroying evil bricks."; + + //EntityTemplate* tmpl; + Texture texture; +} + +__gshared BrickBreakerDemo* demo; + +void brickBreakerRegister() +{ + demo = Mallocator.make!BrickBreakerDemo; + + demo.texture.create(); + demo.texture.load("assets/textures/atlas.png"); + + launcher.manager.beginRegister(); + + registerRenderingModule(launcher.manager); + registerCollisionModule(launcher.manager); + + launcher.manager.registerComponent!CLocation; + launcher.manager.registerComponent!CRotation; + launcher.manager.registerComponent!CScale; + launcher.manager.registerComponent!CTexCoords; + launcher.manager.registerComponent!CTexCoordsIndex; + launcher.manager.registerComponent!CVelocity; + launcher.manager.registerComponent!CInput; + launcher.manager.registerComponent!CPaddle; + launcher.manager.registerComponent!CDamping; + launcher.manager.registerComponent!CVelocityFactor; + launcher.manager.registerComponent!CBall; + launcher.manager.registerComponent!CHitPoints; + + launcher.manager.registerEvent!EDamage; + + launcher.manager.registerSystem!MoveSystem(-100); + launcher.manager.registerSystem!EdgeCollisionSystem(-99); + launcher.manager.registerSystem!BallCollisionSystem(-79); + launcher.manager.registerSystem!InputMovementSystem(-120); + launcher.manager.registerSystem!DampingSystem(-120); + launcher.manager.registerSystem!DamageSystem(-120); + + launcher.manager.endRegister(); +} + +void brickBreakerStart() +{ + DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; + draw_system.default_data.color = 0x80808080; + draw_system.default_data.texture = demo.texture; + draw_system.default_data.size = vec2(16,16); + draw_system.default_data.coords = vec4(246,64,2,2)*px; + draw_system.default_data.material_id = 0; + + EntityTemplate* brick_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CScale.component_id, CColor.component_id, + CTexCoordsIndex.component_id, CBVH.component_id, CHitPoints.component_id, + CAABB.component_id, CStatic.component_id].staticArray + ); + brick_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(304,40,16,8)*px); + brick_tmpl.getComponent!CColor().value = 0x80206020; + brick_tmpl.getComponent!CScale().value = vec2(16,8); + brick_tmpl.getComponent!CHitPoints().value = 2; + //brick_tmpl.getComponent!CAABB().bounding = AABB(vec2(),vec2()); + + EntityTemplate* big_brick_tmpl = launcher.manager.allocateTemplate(brick_tmpl); + big_brick_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(320,32,16,16)*px); + big_brick_tmpl.getComponent!CScale().value = vec2(16,16); + + EntityTemplate* paddle_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CScale.component_id, CInput.component_id, + CTexCoordsIndex.component_id, CPaddle.component_id, CVelocity.component_id, + CDamping.component_id, CVelocityFactor.component_id, CBVH.component_id, + CAABB.component_id].staticArray + ); + paddle_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(272,48,64,10)*px); + paddle_tmpl.getComponent!CScale().value = vec2(64,10); + paddle_tmpl.getComponent!CDamping().value = 14; + paddle_tmpl.getComponent!CVelocityFactor().value = vec2(1,0); + + EntityTemplate* ball_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CScale.component_id, //CDamping.component_id, + CTexCoordsIndex.component_id, CBall.component_id, CVelocity.component_id].staticArray + ); + ball_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(304,32,8,8)*px); + ball_tmpl.getComponent!CScale().value = vec2(8,8); + ball_tmpl.getComponent!CVelocity().value = vec2(0.1,0.1); + // paddle_tmpl.getComponent!CDamping().value = 14; + + launcher.gui_manager.addComponent(CLocation(), "Location"); + launcher.gui_manager.addComponent(CRotation(), "Rotation"); + launcher.gui_manager.addComponent(CScale(), "Scale"); + launcher.gui_manager.addComponent(CColor(), "Color"); + launcher.gui_manager.addComponent(CTexCoords(), "Tex Coords"); + launcher.gui_manager.addComponent(CTexCoordsIndex(), "Tex Coords Index"); + launcher.gui_manager.addComponent(CVelocity(), "Velocity"); + launcher.gui_manager.addComponent(CInput(), "Velocity"); + launcher.gui_manager.addComponent(CDamping(), "Damping"); + launcher.gui_manager.addComponent(CBall(), "Ball"); + launcher.gui_manager.addComponent(CBVH(), "BVH"); + launcher.gui_manager.addComponent(CAABB(), "AABB"); + + launcher.gui_manager.addSystem(MoveSystem.system_id, "Move System"); + launcher.gui_manager.addSystem(EdgeCollisionSystem.system_id, "Edge Collision System"); + launcher.gui_manager.addSystem(BallCollisionSystem.system_id, "Ball Collision System"); + launcher.gui_manager.addSystem(InputMovementSystem.system_id, "Input Movement System"); + launcher.gui_manager.addSystem(DampingSystem.system_id, "Damping System"); + launcher.gui_manager.addSystem(DamageSystem.system_id, "Damage System"); + + launcher.gui_manager.addTemplate(brick_tmpl, "Brick"); + launcher.gui_manager.addTemplate(big_brick_tmpl, "Big Brick"); + launcher.gui_manager.addTemplate(paddle_tmpl, "Paddle"); + launcher.gui_manager.addTemplate(ball_tmpl, "Ball"); + + foreach(i;0..10) + { + CColor color; + final switch(i) + { + case 0:color = 0x80206020;break; + case 1:color = 0x80602020;break; + case 2:color = 0x80202060;break; + case 3:color = 0x80206060;break; + case 4:color = 0x80606020;break; + case 5:color = 0x80602060;break; + case 6:color = 0x80606060;break; + case 7:color = 0x80202020;break; + case 8:color = 0x80008030;break; + case 9:color = 0x80206080;break; + } + foreach (j; 0..20) + { + launcher.manager.addEntity(brick_tmpl,[CLocation(vec2(j*18,300-i*10)).ref_, color.ref_].staticArray); + } + } + + launcher.manager.addEntity(paddle_tmpl,[CLocation(vec2(190,20)).ref_].staticArray); + launcher.manager.addEntity(ball_tmpl,[CLocation(vec2(190,40)).ref_].staticArray); + +} + +void brickBreakerEnd() +{ + demo.texture.destroy(); + + Mallocator.dispose(demo); +} + +void brickBreakerEvent(SDL_Event* event) +{ +} + +bool brickBreakerLoop() +{ + launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5; + + launcher.manager.begin(); + if(launcher.multithreading) + { + launcher.job_updater.begin(); + launcher.manager.updateMT(); + launcher.job_updater.call(); + } + else + { + launcher.manager.update(); + } + launcher.manager.end(); + + return true; +} + +DemoCallbacks getBrickBreakerDemo() +{ + DemoCallbacks demo; + demo.register = &brickBreakerRegister; + demo.initialize = &brickBreakerStart; + demo.deinitialize = &brickBreakerEnd; + demo.loop = &brickBreakerLoop; + demo.tips = .demo.tips; + return demo; +} \ No newline at end of file diff --git a/demos/source/demos/particles.d b/demos/source/demos/particles.d new file mode 100644 index 0000000..ec64774 --- /dev/null +++ b/demos/source/demos/particles.d @@ -0,0 +1,591 @@ +module demos.particles; + +import app; + +import bindbc.sdl; + +import cimgui.cimgui; + +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.entity; +import bubel.ecs.manager; +import bubel.ecs.std; + +import ecs_utils.gfx.texture; +import ecs_utils.math.vector; +import ecs_utils.utils; + +import game_core.basic; +import game_core.rendering; + +import gui.attributes; + +extern(C): + +private enum float px = 1.0/512.0; + +/*####################################################################################################################### +------------------------------------------------ Components ------------------------------------------------------------------ +#######################################################################################################################*/ + +/*struct CLocation +{ + mixin ECS.Component; + + alias location this; + + vec2 location; +} + +struct CColor +{ + mixin ECS.Component; + + alias value this; + + @GUIColor uint value = uint.max; +} + +struct CTexCoords +{ + mixin ECS.Component; + + vec4 value; +}*/ + +// struct CVelocity +// { +// mixin ECS.Component; + +// alias value this; + +// vec2 value = vec2(0); +// } + +struct CForceRange +{ + mixin ECS.Component; + + vec2 range = vec2(20,200); +} + +struct CAttractor +{ + mixin ECS.Component; + + //alias value this; + float strength = 0.2; +} + +struct CVortex +{ + mixin ECS.Component; + + float strength = 0.6; +} + +// struct CDamping +// { +// mixin ECS.Component; + +// alias power this; + +// @GUIRange(0,9) ubyte power = 0; +// } + +struct CGravity +{ + mixin ECS.Component; +} + +struct CParticleLife +{ + mixin ECS.Component; + + this(float life_in_secs) + { + life = cast(int)(life_in_secs * 1000_000); + } + + alias life this; + + int life = 1000000; +} + +/*####################################################################################################################### +------------------------------------------------ Systems ------------------------------------------------------------------ +#######################################################################################################################*/ +/* +struct DrawSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + //uint thread_id; + uint job_id; + //@readonly CTexCoords[] coords; + @readonly CLocation[] locations; + + @optional @readonly CColor[] color; + } + + void onUpdate(EntitiesData data) + { + if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.size = vec2(2,2); + draw_data.coords = vec4(246,64,2,2)*px; + draw_data.color = 0x80808080; + draw_data.material_id = 2; + draw_data.thread_id = data.job_id; + draw_data.texture = particles_demo.texture; + + if(!data.color) + { + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data);//particles_demo.texture, data.locations[i], vec2(2,2), vec4(246,64,2,2)*px, 0, 0x80808080, 0, 2, 0, data.job_id); + } + } + else + { + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i].value; + launcher.renderer.draw(draw_data);//particles_demo.texture, data.locations[i], vec2(2,2), vec4(246,64,2,2)*px, 0, data.color[i].value, 0, 2, 0, data.job_id); + } + } + + } +}*/ + +// struct MoveSystem +// { +// mixin ECS.System!64; + +// struct EntitiesData +// { +// uint length; +// CLocation[] locations; +// @readonly CVelocity[] velocity; +// } + +// void onUpdate(EntitiesData data) +// { +// foreach(i; 0..data.length) +// { +// data.locations[i] += data.velocity[i] * launcher.delta_time; +// } +// } +// } + +struct MouseAttractSystem +{ + mixin ECS.System!64; + + struct EntitiesData + { + uint length; + @readonly CLocation[] locations; + CVelocity[] velocity; + } + + vec2 mouse_pos; + + bool onBegin() + { + if(!launcher.getKeyState(SDL_SCANCODE_SPACE))return false; + mouse_pos = launcher.mouse.position; + mouse_pos = vec2(mouse_pos.x, mouse_pos.y) * launcher.scalling - launcher.render_position; + return true; + } + + void onUpdate(EntitiesData data) + { + float speed = launcher.delta_time * 0.01; + foreach(i;0..data.length) + { + vec2 rel_pos = mouse_pos - data.locations[i]; + float len2 = rel_pos.x * rel_pos.x + rel_pos.y * rel_pos.y; + if(len2 < 0.1)len2 = 0.1; + data.velocity[i] = data.velocity[i] + rel_pos / len2 * speed; + } + } +} + +struct AttractSystem +{ + mixin ECS.System!64; + + struct EntitiesData + { + uint length; + @readonly CLocation[] locations; + CVelocity[] velocity; + } + + struct Updater + { + AttractSystem.EntitiesData data; + + void onUpdate(AttractorIterator.EntitiesData adata) + { + float speed = launcher.delta_time * 0.00004; + if(adata.vortex) + { + foreach(i;0..data.length) + { + foreach(j;0..adata.length) + { + vec2 rel_pos = data.locations[i] - adata.locations[j]; + float len2 = rel_pos.length2(); + float inv_len = rsqrt(len2); + + if(1 < adata.force_range[j].range.y*inv_len) + { + float dist = (adata.force_range[j].range.y - 0.4)*inv_len - 1; + + vec2 vec = rel_pos * inv_len; + vec2 cvec = vec2(-vec.y,vec.x); + + float sign = -1; + if(1 < adata.force_range[j].range.x*inv_len)sign = 1; + + float str = adata.attractor[j].strength * sign; + float vortex_str = adata.vortex[j].strength; + data.velocity[i] = data.velocity[i] + (rel_pos * str + cvec * vortex_str) * speed * dist; + } + } + } + } + else + { + foreach(i;0..data.length) + { + foreach(j;0..adata.length) + { + vec2 rel_pos = data.locations[i] - adata.locations[j]; + float len2 = rel_pos.length2(); + float inv_len = rsqrt(len2); + + if(1 < adata.force_range[j].range.y*inv_len) + { + float dist = (adata.force_range[j].range.y - 0.4)*inv_len - 1; + + vec2 vec = rel_pos; + + float sign = -1; + if(1 < adata.force_range[j].range.x*inv_len)sign = 1; + + float str = adata.attractor[j].strength * speed * dist * sign; + data.velocity[i] = data.velocity[i] + vec * str; + } + } + } + } + } + } + + void onUpdate(EntitiesData data) + { + Updater updater; + updater.data = data; + launcher.manager.callEntitiesFunction!AttractorIterator(&updater.onUpdate); + } +} + +struct AttractorIterator +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + @readonly CLocation[] locations; + @readonly CAttractor[] attractor; + @readonly CForceRange[] force_range; + @optional @readonly CVortex[] vortex; + } + + bool onBegin() + { + return false; + } + + void onUpdate(EntitiesData data) + { + + } +} + +struct PlayAreaSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + Entity[] entity; + @readonly CLocation[] locations; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + if(data.locations[i].x > 440)launcher.manager.removeEntity(data.entity[i].id); + else if(data.locations[i].x < -40)launcher.manager.removeEntity(data.entity[i].id); + if(data.locations[i].y > 340)launcher.manager.removeEntity(data.entity[i].id); + else if(data.locations[i].y < -40)launcher.manager.removeEntity(data.entity[i].id); + } + } +} + +// struct DampingSystem +// { +// mixin ECS.System!32; + +// struct EntitiesData +// { +// uint length; +// const (Entity)[] entity; +// @readonly CDamping[] damping; +// CVelocity[] velocity; +// } + +// float[10] damp = 0; + +// bool onBegin() +// { +// foreach(i;0..10) +// { +// damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1); +// } + +// return true; +// } + +// void onUpdate(EntitiesData data) +// { +// foreach(i; 0..data.length) +// { +// data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; +// } +// } +// } + +struct ParticleLifeSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + CParticleLife[] life; + } + + int delta_time; + + bool onBegin() + { + delta_time = cast(int)(launcher.delta_time * 1000); + return true; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + data.life[i] -= delta_time; + if(data.life[i] < 0)launcher.manager.removeEntity(data.entity[i].id); + } + } +} + +struct GravitySystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + @readonly CGravity[] gravity; + CVelocity[] velocity; + } + + void onUpdate(EntitiesData data) + { + float delta_time = launcher.delta_time * 0.00_092; + foreach(i; 0..data.length) + { + data.velocity[i].y -= delta_time; + } + } +} + +/*####################################################################################################################### +------------------------------------------------ Functions ------------------------------------------------------------------ +#######################################################################################################################*/ + +struct ParticlesDemo +{ + __gshared const (char)* tips = "Use \"space\" to spwan entities.\n\nSystems can be enabled/disabled from \"Simple\" window."; + + Texture texture; +} + +__gshared ParticlesDemo* particles_demo; + +void particlesRegister() +{ + particles_demo = Mallocator.make!ParticlesDemo; + + particles_demo.texture.create(); + particles_demo.texture.load("assets/textures/atlas.png"); + + launcher.manager.beginRegister(); + + registerRenderingModule(launcher.manager); + + launcher.manager.registerComponent!CLocation; + //launcher.manager.registerComponent!CTexCoords; + launcher.manager.registerComponent!CColor; + launcher.manager.registerComponent!CVelocity; + launcher.manager.registerComponent!CScale; + launcher.manager.registerComponent!CTexCoords; + launcher.manager.registerComponent!CTexCoordsIndex; + launcher.manager.registerComponent!CRotation; + launcher.manager.registerComponent!CDepth; + launcher.manager.registerComponent!CAttractor; + launcher.manager.registerComponent!CDamping; + launcher.manager.registerComponent!CGravity; + launcher.manager.registerComponent!CVortex; + launcher.manager.registerComponent!CParticleLife; + launcher.manager.registerComponent!CForceRange; + launcher.manager.registerComponent!CMaterialIndex; + + launcher.manager.registerSystem!MoveSystem(0); + launcher.manager.registerSystem!DrawSystem(100); + launcher.manager.registerSystem!PlayAreaSystem(102); + launcher.manager.registerSystem!AttractSystem(-1); + launcher.manager.registerSystem!MouseAttractSystem(1); + launcher.manager.registerSystem!DampingSystem(101); + launcher.manager.registerSystem!ParticleLifeSystem(-10); + launcher.manager.registerSystem!GravitySystem(-2); + + launcher.manager.registerSystem!AttractorIterator(-1); + + launcher.manager.endRegister(); +} + +void particlesStart() +{ + DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; + draw_system.default_data.size = vec2(2,2); + draw_system.default_data.coords = vec4(246,64,2,2)*px; + draw_system.default_data.material_id = 2; + draw_system.default_data.texture = particles_demo.texture; + + launcher.gui_manager.addSystem(MoveSystem.system_id,"Move System"); + launcher.gui_manager.addSystem(DrawSystem.system_id,"Draw System"); + launcher.gui_manager.addSystem(PlayAreaSystem.system_id,"Play Area System"); + launcher.gui_manager.addSystem(AttractSystem.system_id,"Attract System"); + launcher.gui_manager.addSystem(MouseAttractSystem.system_id,"Mouse Attract System"); + launcher.gui_manager.addSystem(DampingSystem.system_id,"Damping System"); + launcher.gui_manager.addSystem(ParticleLifeSystem.system_id,"Particle Life System"); + + // launcher.gui_manager.addComponent(CColor(),"Color (white)"); + // launcher.gui_manager.addComponent(CColor(0xFF101540),"Color (red)"); + // launcher.gui_manager.addComponent(CColor(0xFF251010),"Color (blue)"); + // launcher.gui_manager.addComponent(CColor(0xFF102010),"Color (green)"); + launcher.gui_manager.addComponent(CColor(0xFF101540),"Color"); + launcher.gui_manager.addComponent(CAttractor(0.1),"Attractor"); + launcher.gui_manager.addComponent(CForceRange(vec2(5,40)),"ForceRange"); + launcher.gui_manager.addComponent(CVelocity(),"Velocity"); + launcher.gui_manager.addComponent(CDamping(),"Damping"); + launcher.gui_manager.addComponent(CVortex(),"Vortex"); + launcher.gui_manager.addComponent(CParticleLife(),"Particle Life"); + launcher.gui_manager.addComponent(CGravity(),"Gravity"); + + EntityTemplate* tmpl; + EntityTemplate* base_tmpl = launcher.manager.allocateTemplate([CTexCoords.component_id, CLocation.component_id, CColor.component_id, CVelocity.component_id, CDamping.component_id, CScale.component_id, CMaterialIndex.component_id].staticArray); + base_tmpl.getComponent!CColor().value = 0xFF251010; + base_tmpl.getComponent!CScale().value = vec2(2); + base_tmpl.getComponent!CTexCoords().value = vec4(246,64,2,2)*px; + base_tmpl.getComponent!CMaterialIndex().value = 2; + launcher.gui_manager.addTemplate(base_tmpl,"Particle"); + // tmpl = launcher.manager.allocateTemplate(base_tmpl); + // tmpl.getComponent!CColor().value = 0xFF251010; + // launcher.gui_manager.addTemplate(tmpl,"Particle (blue)"); + // tmpl = launcher.manager.allocateTemplate(base_tmpl); + // tmpl.getComponent!CColor().value = 0xFF102010; + // launcher.gui_manager.addTemplate(tmpl,"Particle (green)"); + // tmpl = launcher.manager.allocateTemplate(base_tmpl); + // tmpl.getComponent!CColor().value = 0xFF101540; + // launcher.gui_manager.addTemplate(tmpl,"Particle (red)"); + // tmpl = launcher.manager.allocateTemplate(tmpl, [CDamping.component_id].staticArray); + // launcher.gui_manager.addTemplate(tmpl,"Particle (damping)"); + // tmpl = launcher.manager.allocateTemplate(tmpl); + // tmpl.getComponent!CDamping().power = 4; + // launcher.gui_manager.addTemplate(tmpl,"Particle (damping!)"); + tmpl = launcher.manager.allocateTemplate([CAttractor.component_id, CLocation.component_id, CForceRange.component_id, CScale.component_id].staticArray); + tmpl.getComponent!CScale().value = vec2(4); + launcher.gui_manager.addTemplate(tmpl,"Attractor"); + tmpl = launcher.manager.allocateTemplate(tmpl, [CVortex.component_id].staticArray); + launcher.gui_manager.addTemplate(tmpl,"Vortex"); + // tmpl = launcher.manager.allocateTemplate(tmpl); + // tmpl.getComponent!CVortex().strength = -0.6; + // launcher.gui_manager.addTemplate(tmpl,"Vortex (reversed)"); + +} + +void particlesEnd() +{ + particles_demo.texture.destroy(); + + //launcher.manager.freeTemplate(simple.tmpl); + Mallocator.dispose(particles_demo); +} + +void particlesEvent(SDL_Event* event) +{ +} + +bool particlesLoop() +{ + launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5; + + launcher.manager.begin(); + if(launcher.multithreading) + { + launcher.job_updater.begin(); + launcher.manager.updateMT(); + launcher.job_updater.call(); + } + else + { + launcher.manager.update(); + } + launcher.manager.end(); + + return true; +} + +DemoCallbacks getParticlesDemo() +{ + DemoCallbacks demo; + demo.register = &particlesRegister; + demo.initialize = &particlesStart; + demo.deinitialize = &particlesEnd; + demo.loop = &particlesLoop; + demo.tips = ParticlesDemo.tips; + return demo; +} \ No newline at end of file diff --git a/demos/source/demos/sandbox.d b/demos/source/demos/sandbox.d new file mode 100644 index 0000000..925ebb2 --- /dev/null +++ b/demos/source/demos/sandbox.d @@ -0,0 +1,101 @@ +module demos.sandbox; + +import bindbc.sdl; + +import demos.simple; +import demos.snake; +import demos.space_invaders; +import demos.particles; +import demos.brick_breaker; + +import game_core.rendering; + +import app; + +import ecs_utils.math.vector; + +extern(C): + +void sandboxStart() +{ + simpleRegister(); + snakeRegister(); + spaceInvadersRegister(); + particlesRegister(); + brickBreakerRegister(); + + simpleStart(); + snakeStart(); + spaceInvadersStart(); + particlesStart(); + brickBreakerStart(); + + DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; + draw_system.default_data.size = vec2(16,16); + draw_system.default_data.coords = vec4(0,48,16,16)*demos.simple.px; + draw_system.default_data.material_id = 0; + draw_system.default_data.texture = particles_demo.texture; + draw_system.default_data.color = 0x80808080; + + launcher.manager.getSystem(MouseAttractSystem.system_id).disable(); + launcher.manager.getSystem(demos.simple.MoveSystem.system_id).disable(); + +} + +void sandboxEnd() +{ + simpleEnd(); + snakeEnd(); + spaceInvadersEnd(); + particlesEnd(); +} +/* +void sandboxEvent(SDL_Event* event) +{ +}*/ + +bool sandboxLoop() +{ + launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5; + + launcher.manager.begin(); + + float delta_time = launcher.delta_time; + if(delta_time > 2000)delta_time = 2000; + __gshared float time = 0; + + /*if(launcher.getKeyState(SDL_SCANCODE_SPACE))time += delta_time * 3; + else */ + time += delta_time; + + while(time > 200) + { + time -= 200; + + launcher.manager.update("fixed"); + } + + if(launcher.multithreading) + { + launcher.job_updater.begin(); + launcher.manager.updateMT(); + launcher.job_updater.call(); + } + else + { + launcher.manager.update(); + } + launcher.manager.end(); + + return true; +} + +DemoCallbacks getSanboxDemo() +{ + DemoCallbacks demo; + demo.initialize = &sandboxStart; + demo.deinitialize = &sandboxEnd; + demo.loop = &sandboxLoop; + demo.tips = "tips"; + return demo; +} \ No newline at end of file diff --git a/demos/source/demos/simple.d b/demos/source/demos/simple.d index 313cc00..ecccc12 100644 --- a/demos/source/demos/simple.d +++ b/demos/source/demos/simple.d @@ -4,47 +4,42 @@ import app; import bindbc.sdl; -import cimgui.cimgui; - import bubel.ecs.attributes; import bubel.ecs.core; import bubel.ecs.entity; import bubel.ecs.manager; import bubel.ecs.std; +import cimgui.cimgui; + import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +import game_core.basic; +import game_core.rendering; + extern(C): -struct Simple -{ - __gshared const (char)* tips = "Use \"space\" to spwan entities.\n\nSystems can be enabled/disabled from \"Simple\" window."; +enum float px = 1.0/512.0; - EntityTemplate* tmpl; - Texture texture; +/*####################################################################################################################### +------------------------------------------------ Components ------------------------------------------------------------------ +#######################################################################################################################*/ - bool move_system = true; - bool draw_system = true; -} - -struct CLocation +/*struct CLocation { mixin ECS.Component; alias location this; vec2 location; -} - -struct CTexture -{ - mixin ECS.Component; - - Texture tex; -} +}*/ +/*####################################################################################################################### +------------------------------------------------ Systems ------------------------------------------------------------------ +#######################################################################################################################*/ +/* struct DrawSystem { mixin ECS.System!32; @@ -54,22 +49,29 @@ struct DrawSystem uint length; //uint thread_id; uint job_id; - @readonly CTexture[] textures; @readonly CLocation[] locations; } void onUpdate(EntitiesData data) { if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.size = vec2(16,16); + draw_data.coords = vec4(0,0,1,1); + draw_data.color = 0x80808080; + draw_data.material_id = 0; + draw_data.thread_id = data.job_id; + draw_data.texture = simple.texture; + foreach(i; 0..data.length) { - launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), cast(ushort)(data.locations[i].y), 0x80808080, 0, 0, 0, data.job_id); - // launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), 0, 0x80808080, 0, 0, 0, data.job_id); - //draw(renderer, data.textures[i].tex, data.locations[i], vec2(32,32), vec4(0,0,1,1)); + draw_data.position = data.locations[i]; + draw_data.depth = cast(ushort)(data.locations[i].y); + launcher.renderer.draw(draw_data); } - //if(data.thread_id == 0)launcher.renderer.pushData(); } -} +}*/ struct MoveSystem { @@ -85,47 +87,67 @@ struct MoveSystem { foreach(i; 0..data.length) { - data.locations[i].location.y = data.locations[i].location.y + 1; - if(data.locations[i].location.y > 300)data.locations[i].location.y = 0; + data.locations[i].y = data.locations[i].y + 1; + if(data.locations[i].y > 300)data.locations[i].y = 0; } } } +/*####################################################################################################################### +------------------------------------------------ Functions ------------------------------------------------------------------ +#######################################################################################################################*/ + +struct Simple +{ + __gshared const (char)* tips = "Use \"space\" to spwan entities.\n\nSystems can be enabled/disabled from \"Simple\" window."; + + EntityTemplate* tmpl; + Texture texture; +} + __gshared Simple* simple; -void simpleStart() +void simpleRegister() { simple = Mallocator.make!Simple; simple.texture.create(); - simple.texture.load("assets/textures/buckler.png"); + simple.texture.load("assets/textures/atlas.png"); launcher.manager.beginRegister(); + registerRenderingModule(launcher.manager); + launcher.manager.registerComponent!CLocation; - launcher.manager.registerComponent!CTexture; launcher.manager.registerSystem!MoveSystem(0); - launcher.manager.registerSystem!DrawSystem(1); + // launcher.manager.registerSystem!DrawSystem(1); launcher.manager.endRegister(); +} - launcher.gui_manager.addSystem(MoveSystem.system_id,"Move System"); +void simpleStart() +{ + DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; + draw_system.default_data.color = 0x80808080; + draw_system.default_data.texture = simple.texture; + draw_system.default_data.size = vec2(16,16); + draw_system.default_data.coords = vec4(0,48,16,16)*px;//vec4(0,0,1,1); + + launcher.gui_manager.addSystem(MoveSystem.system_id,"Move Up System"); launcher.gui_manager.addSystem(DrawSystem.system_id,"Draw System"); - ushort[2] components = [CLocation.component_id, CTexture.component_id]; - simple.tmpl = launcher.manager.allocateTemplate(components); - CTexture* tex_comp = simple.tmpl.getComponent!CTexture; - tex_comp.tex = simple.texture; - CLocation* loc_comp = simple.tmpl.getComponent!CLocation; + simple.tmpl = launcher.manager.allocateTemplate([CLocation.component_id, CDrawDefault.component_id].staticArray); + //*simple.tmpl.getComponent!CTexCoordsIndex = TexCoordsManager.instance.getCoordIndex(vec4(0,48,16,16)*px); + //CLocation* loc_comp = simple.tmpl.getComponent!CLocation; launcher.gui_manager.addTemplate(simple.tmpl, "Basic"); foreach(i; 0..10) foreach(j; 0..10) { - loc_comp.location = vec2(i*16+64,j*16+64); - launcher.manager.addEntity(simple.tmpl); + //loc_comp.value = vec2(i*16+64,j*16+64); + launcher.manager.addEntity(simple.tmpl,[CLocation(vec2(i*16+64,j*16+64)).ref_].staticArray); } } @@ -140,46 +162,15 @@ void simpleEnd() Mallocator.dispose(simple); } -void simpleTool(vec2 position, Tool tool, int size) -{ - switch(tool) - { - case Tool.entity_spawner: - { - EntityTemplate* tmpl = launcher.gui_manager.getSelectedTemplate(); - CLocation* location = tmpl.getComponent!CLocation; - if(location) - { - position.x += (randomf - 0.5) * size; - position.y += (randomf - 0.5) * size; - if(position.x > 400)position.x -= 400; - else if(position.x < 0)position.x += 400; - if(position.y > 300)position.y -= 300; - else if(position.y < 0)position.y += 300; - *location = position; - } - launcher.manager.addEntity(tmpl); - } - break; - default: - break; - } -} - void simpleEvent(SDL_Event* event) { - /*if(event.type == event.button) - { - vec2 position = vec2(event.button.x, event.button.y); - - }*/ } void spawnEntity() { - CLocation* loc_comp = simple.tmpl.getComponent!CLocation; - loc_comp.location = vec2(randomf() * 400,0); - launcher.manager.addEntity(simple.tmpl); + //CLocation* loc_comp = simple.tmpl.getComponent!CLocation; + //loc_comp.value = vec2(randomf() * 400,0); + launcher.manager.addEntity(simple.tmpl,[CLocation(vec2(randomf() * 400,0)).ref_].staticArray); } bool simpleLoop() @@ -188,7 +179,7 @@ bool simpleLoop() if(launcher.getKeyState(SDL_SCANCODE_SPACE)) { - foreach(i;0..1)spawnEntity(); + foreach(i;0..20)spawnEntity(); } launcher.manager.begin(); @@ -205,4 +196,15 @@ bool simpleLoop() launcher.manager.end(); return true; +} + +DemoCallbacks getSimpleDemo() +{ + DemoCallbacks demo; + demo.register = &simpleRegister; + demo.initialize = &simpleStart; + demo.deinitialize = &simpleEnd; + demo.loop = &simpleLoop; + demo.tips = simple.tips; + return demo; } \ No newline at end of file diff --git a/demos/source/demos/snake.d b/demos/source/demos/snake.d index 01e9a36..865ab75 100644 --- a/demos/source/demos/snake.d +++ b/demos/source/demos/snake.d @@ -4,8 +4,6 @@ import app; import bindbc.sdl; -import cimgui.cimgui; - import bubel.ecs.attributes; import bubel.ecs.core; import bubel.ecs.entity; @@ -13,10 +11,14 @@ import bubel.ecs.manager; import bubel.ecs.std; import bubel.ecs.vector; +import cimgui.cimgui; + import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +import game_core.basic; + //import std.array : staticArray; enum float px = 1.0/512.0; @@ -31,22 +33,6 @@ struct MapElement apple = 1, wall = 2, snake = 3, - - /* snake_head_up = 5, - snake_head_down = 6, - snake_head_left = 7, - snake_head_right = 8, - snake_tail_up = 9, - snake_tail_down = 10, - snake_tail_left = 11, - snake_tail_right = 12, - snake_turn_ld = 13, - snake_turn_lu = 14, - snake_turn_rd = 15, - snake_turn_ru = 16, - snake_vertical = 17, - snake_horizontal = 18*/ - } Type type; EntityID id; @@ -86,7 +72,7 @@ struct Snake bool move_system = true; bool draw_system = true; - const int map_size = 18; + enum int map_size = 18; MapElement[map_size * map_size] map; @@ -97,7 +83,7 @@ struct Snake if(apple_tmpl)launcher.manager.freeTemplate(apple_tmpl); if(snake_tmpl)launcher.manager.freeTemplate(snake_tmpl); if(snake_destroy_particle)launcher.manager.freeTemplate(snake_destroy_particle); - texture.destory(); + texture.destroy(); } MapElement element(ivec2 pos) @@ -129,10 +115,7 @@ struct Snake } if(base_pos.x == random_pos.x && base_pos.y == random_pos.y)return; } - //CILocation* location = apple_tmpl.getComponent!CILocation; - //*location = random_pos; - //Entity* apple = - launcher.manager.addEntity(apple_tmpl,[CILocation(random_pos).ref_].staticArray); + launcher.manager.addEntity(apple_tmpl,[CLocation(cast(vec2)(random_pos)*16).ref_].staticArray); } } @@ -158,14 +141,14 @@ struct CILocation ivec2 location; } -struct CLocation -{ - mixin ECS.Component; +// struct CLocation +// { +// mixin ECS.Component; - alias location this; +// alias location this; - vec2 location = vec2(0,0); -} +// vec2 location = vec2(0,0); +// } struct CSnake { @@ -252,10 +235,10 @@ struct CMovement Direction direction; } -struct CInput -{ - mixin ECS.Component; -} +// struct CInput +// { +// mixin ECS.Component; +// } struct AppleSystem { @@ -264,8 +247,8 @@ struct AppleSystem struct EntitiesData { uint length; - @readonly Entity[] entities; - @readonly CApple[] movement; + @readonly Entity[] entity; + @readonly CApple[] apple; @readonly CILocation[] location; } @@ -273,7 +256,17 @@ struct AppleSystem { foreach(i;0..data.length) { - snake.element(MapElement(MapElement.Type.apple,data.entities[i].id),data.location[i]); + if(snake.element(data.location[i]).id == EntityID())snake.element(MapElement(MapElement.Type.apple,data.entity[i].id),data.location[i]); + else launcher.manager.removeEntity(data.entity[i].id); + } + } + + void onRemoveEntity(EntitiesData data) + { + foreach(i;0..data.length) + { + if(snake.element(data.location[i].location).id == data.entity[i].id) + snake.element(MapElement(MapElement.Type.empty, EntityID()),data.location[i].location); } } } @@ -315,7 +308,7 @@ struct ParticleMovementSystem { foreach(i;0..data.length) { - data.location[i].location -= data.movement[i].velocity; + data.location[i] -= data.movement[i].velocity; } } } @@ -355,9 +348,21 @@ struct AnimationRenderSystem void onUpdate(EntitiesData data) { + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.size = vec2(16,16); + //draw_data.coords = vec4(0,0,1,1)*px; + draw_data.color = 0x80808080; + draw_data.material_id = 0; + draw_data.thread_id = 0; + draw_data.texture = snake.texture; + draw_data.depth = -1; foreach(i;0..data.length) { - launcher.renderer.draw(snake.texture, cast(vec2)cast(ivec2)data.location[i].location, vec2(16,16), data.animation[i].frames[cast(int)(data.animation[i].time)], -1, 0x80808080); + draw_data.position = data.location[i]; + draw_data.coords = data.animation[i].frames[cast(int)(data.animation[i].time)]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(snake.texture, cast(vec2)cast(ivec2)data.location[i], vec2(16,16), data.animation[i].frames[cast(int)(data.animation[i].time)], -1, 0x80808080); } } } @@ -477,7 +482,12 @@ struct MoveSystem break; case MapElement.Type.apple: launcher.manager.removeEntity(snake.element(data.location[i].location).id); - if(data.snakes[i].parts.length < 100)data.snakes[i].parts.add(new_location); + if(data.snakes[i].parts.length >= 99) + { + snake.addApple(); + goto case(MapElement.Type.empty); + } + data.snakes[i].parts.add(new_location); if(data.snakes[i].parts.length > 1) { @@ -506,7 +516,40 @@ struct MoveSystem } } } - + } +} + +struct SnakeSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + uint length; + Entity[] entity; + @readonly CSnake[] snake; + @readonly CILocation[] location; + } + + void onAddSystem(EntitiesData data) + { + foreach(i;0..data.length) + { + if(snake.element(data.location[i]).id == EntityID())snake.element(MapElement(MapElement.Type.snake,data.entity[i].id),data.location[i]); + else launcher.manager.removeEntity(data.entity[i].id); + } + } + + void onRemoveEntity(EntitiesData data) + { + foreach(i;0..data.length) + { + if(snake.element(data.location[i].location).id == data.entity[i].id) + snake.element(MapElement(MapElement.Type.empty, EntityID()),data.location[i].location); + foreach(part; data.snake[i].parts.array) + if(snake.element(part).id == data.entity[i].id) + snake.element(MapElement(MapElement.Type.empty, EntityID()),part); + } } } @@ -618,9 +661,19 @@ struct DrawAppleSystem void onUpdate(EntitiesData data) { + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.size = vec2(16,16); + draw_data.coords = vec4(0,32*px,16*px,16*px); + draw_data.color = 0x80808080; + draw_data.material_id = 0; + draw_data.thread_id = 0; + draw_data.texture = snake.texture; foreach(i; 0..data.location.length) { - launcher.renderer.draw(snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0x80808080, 0); + draw_data.position = vec2(data.location[i].x*16,data.location[i].y*16); + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0x80808080, 0); } } } @@ -688,34 +741,49 @@ struct DrawSnakeSystem static void drawElement(ivec2 loc, SnakePart part) { + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.size = vec2(16,16); + draw_data.color = 0x80808080; + draw_data.texture = snake.texture; + draw_data.position = cast(vec2)loc; final switch(cast(ubyte)part) { - case SnakePart.tail_up:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,112,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.tail_down:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(0,112,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.tail_left:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(32,112,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.tail_right:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(0,144,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.turn_ld:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(64,128,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.turn_lu:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(32,144,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.turn_rd:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,144,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.turn_ru:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(64,112,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.vertical:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,128,16,16)*px, 0, 0x80808080, 0);break; - case SnakePart.horizontal:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(48,128,16,16)*px, 0, 0x80808080, 0);break; + case SnakePart.tail_up:draw_data.coords = vec4(16,112,16,16)*px;break; + case SnakePart.tail_down:draw_data.coords = vec4(0,112,16,16)*px;break; + case SnakePart.tail_left:draw_data.coords = vec4(32,112,16,16)*px;break; + case SnakePart.tail_right:draw_data.coords = vec4(0,144,16,16)*px;break; + case SnakePart.turn_ld:draw_data.coords = vec4(64,128,16,16)*px;break; + case SnakePart.turn_lu:draw_data.coords = vec4(32,144,16,16)*px;break; + case SnakePart.turn_rd:draw_data.coords = vec4(16,144,16,16)*px;break; + case SnakePart.turn_ru:draw_data.coords = vec4(64,112,16,16)*px;break; + case SnakePart.vertical:draw_data.coords = vec4(16,128,16,16)*px;break; + case SnakePart.horizontal:draw_data.coords = vec4(48,128,16,16)*px;break; } + launcher.renderer.draw(draw_data); } void onUpdate(EntitiesData data) { + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.size = vec2(16,16); + draw_data.color = 0x80808080; + draw_data.texture = snake.texture; + foreach(i; 0..data.length) { const (CSnake)* snake = &data.snake[i]; scope vec2 loc = cast(vec2)(data.location[i].location * 16); + draw_data.position = loc; final switch(snake.direction) { - case CMovement.Direction.up:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(48,112,16,16)*px, 0, 0x80808080, 0);break; - case CMovement.Direction.down:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(48,144,16,16)*px, 0, 0x80808080, 0);break; - case CMovement.Direction.left:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,128,16,16)*px, 0, 0x80808080, 0);break; - case CMovement.Direction.right:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(32,128,16,16)*px, 0, 0x80808080, 0);break; + case CMovement.Direction.up:draw_data.coords = vec4(48,112,16,16)*px;break; + case CMovement.Direction.down:draw_data.coords = vec4(48,144,16,16)*px;break; + case CMovement.Direction.left:draw_data.coords = vec4(0,128,16,16)*px;break; + case CMovement.Direction.right:draw_data.coords = vec4(32,128,16,16)*px;break; } + launcher.renderer.draw(draw_data); if(snake.parts.length >1) { foreach(j;1..snake.parts.length - 1)drawElement(snake.parts[j]*16, snakePart(snake.parts[j], snake.parts[j+1], snake.parts[j-1])); @@ -750,10 +818,41 @@ struct CleanSystem } } +struct CopyLocationSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + CLocation[] location; + @readonly CILocation[] ilocation; + } + + void onAddEntity(EntitiesData data) + { + foreach(i;0..data.length) + { + data.ilocation[i] = cast(ivec2)(data.location[i] / 16); + } + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.location[i] = cast(vec2)(data.ilocation[i] * 16); + } + } +} + __gshared Snake* snake; -void snakeStart() +void snakeRegister() { + import game_core.rendering; + snake = Mallocator.make!Snake; snake.texture.create(); @@ -763,6 +862,8 @@ void snakeStart() launcher.manager.registerPass("fixed"); + registerRenderingModule(launcher.manager); + launcher.manager.registerComponent!CLocation; launcher.manager.registerComponent!CILocation; launcher.manager.registerComponent!CSnake; @@ -776,7 +877,6 @@ void snakeStart() launcher.manager.registerSystem!MoveSystem(0,"fixed"); launcher.manager.registerSystem!InputSystem(-100); launcher.manager.registerSystem!FixSnakeDirectionSystem(-1,"fixed"); - launcher.manager.registerSystem!AppleSystem(-1,"fixed"); launcher.manager.registerSystem!AnimationRenderSystem(100); launcher.manager.registerSystem!AnimationSystem(-1); launcher.manager.registerSystem!ParticleSystem(-1); @@ -784,7 +884,24 @@ void snakeStart() launcher.manager.registerSystem!DrawAppleSystem(99); launcher.manager.registerSystem!DrawSnakeSystem(101); + launcher.manager.registerSystem!CopyLocationSystem(100); + //launcher.manager.registerSystem!AppleRemoveSystem(100); + launcher.manager.registerSystem!AppleSystem(101); + launcher.manager.registerSystem!SnakeSystem(101); + launcher.manager.endRegister(); +} + +void snakeStart() +{ + launcher.gui_manager.addComponent(CApple(),"Apple"); + launcher.gui_manager.addComponent(CSnake(),"Snake"); + launcher.gui_manager.addComponent(CParticle(1000),"Particle"); + launcher.gui_manager.addComponent(CParticleVector(vec2(0,1)),"Particle Vector"); + launcher.gui_manager.addComponent(CInput(),"Input"); + launcher.gui_manager.addComponent(CMovement(CMovement.Direction.up),"Movement"); + //launcher.gui_manager.addComponent(CAnimation(),"Movement"); + launcher.gui_manager.addComponent(CILocation(),"Int Location"); launcher.gui_manager.addSystem(MoveSystem.system_id,"Move System"); launcher.gui_manager.addSystem(InputSystem.system_id,"Input System"); @@ -793,19 +910,19 @@ void snakeStart() launcher.gui_manager.addSystem(AnimationSystem.system_id,"Animation System"); launcher.gui_manager.addSystem(ParticleSystem.system_id,"Particle Life System"); launcher.gui_manager.addSystem(ParticleMovementSystem.system_id,"Particle Movement System"); + launcher.gui_manager.addSystem(DrawAppleSystem.system_id,"Draw Apple System"); + launcher.gui_manager.addSystem(DrawSnakeSystem.system_id,"Draw Snake System"); snake.snake_destroy_particle_frames = Mallocator.makeArray([vec4(64,144,16,16)*px,vec4(80,144,16,16)*px,vec4(96,144,16,16)*px,vec4(112,144,16,16)*px].staticArray); { - ushort[4] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id]; + ushort[5] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id, CLocation.component_id]; snake.snake_tmpl = launcher.manager.allocateTemplate(components); - //CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation; - //*loc_comp = ivec2(2,2); launcher.manager.addEntity(snake.snake_tmpl,[CILocation(ivec2(2,2)).ref_].staticArray); } { - snake.snake_destroy_particle = launcher.manager.allocateTemplate([CLocation.component_id, CParticle.component_id, CParticleVector.component_id, CAnimation.component_id].staticArray); + snake.snake_destroy_particle = launcher.manager.allocateTemplate([CLocation.component_id, CParticle.component_id, CParticleVector.component_id, CAnimation.component_id, CLocation.component_id].staticArray); CAnimation* canim = snake.snake_destroy_particle.getComponent!CAnimation; canim.frames = snake.snake_destroy_particle_frames; CParticle* particle = snake.snake_destroy_particle.getComponent!CParticle; @@ -813,7 +930,7 @@ void snakeStart() } { - ushort[2] components = [CILocation.component_id, CApple.component_id]; + ushort[3] components = [CILocation.component_id, CApple.component_id, CLocation.component_id]; snake.apple_tmpl = launcher.manager.allocateTemplate(components); snake.addApple(); } @@ -824,13 +941,6 @@ void snakeStart() MoveSystem* move_system = launcher.manager.getSystem!MoveSystem(); move_system.setTemplates(); - - /*foreach(i; 0..10) - foreach(j; 0..10) - { - loc_compation = vec2(i*32+64,j*32+64); - launcher.manager.addEntity(simple.tmpl); - }*/ } void snakeEnd() @@ -839,39 +949,6 @@ void snakeEnd() Mallocator.dispose(snake); } -void snakeTool(vec2 position, Tool tool, int size) -{ - switch(tool) - { - case Tool.entity_spawner: - { - EntityTemplate* tmpl = launcher.gui_manager.getSelectedTemplate(); - CLocation* location = tmpl.getComponent!CLocation; - if(location) - { - position.x += (randomf() - 0.5) * size; - position.y += (randomf() - 0.5) * size; - *location = position; - } - CILocation* ilocation = tmpl.getComponent!CILocation; - if(ilocation) - { - position.x += (randomf() - 0.5) * size; - position.y += (randomf() - 0.5) * size; - ivec2 ipos; - ipos.x = cast(int)(position.x / 16); - ipos.y = cast(int)(position.y / 16); - *ilocation = ipos; - if(snake.element(ipos).type != MapElement.Type.empty)return; - } - launcher.manager.addEntity(tmpl); - } - break; - default: - break; - } -} - void snakeEvent(SDL_Event* event) { @@ -881,16 +958,16 @@ bool snakeLoop() { launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(288,288)) * 0.5; - /*if(launcher.show_demo_wnd) - { - igSetNextWindowPos(ImVec2(800 - 260, 30), ImGuiCond_Once, ImVec2(0,0)); - igSetNextWindowSize(ImVec2(250, 0), ImGuiCond_Once); - if(igBegin("Snake",&launcher.show_demo_wnd,0)) - { + // if(launcher.show_demo_wnd) + // { + // igSetNextWindowPos(ImVec2(800 - 260, 30), ImGuiCond_Once, ImVec2(0,0)); + // igSetNextWindowSize(ImVec2(250, 0), ImGuiCond_Once); + // if(igBegin("Snake",&launcher.show_demo_wnd,0)) + // { - } - igEnd(); - }*/ + // } + // igEnd(); + // } launcher.manager.begin(); @@ -901,9 +978,9 @@ bool snakeLoop() if(launcher.getKeyState(SDL_SCANCODE_SPACE))time += delta_time * 3; else time += delta_time; - while(time > 100) + while(time > 200) { - time -= 100; + time -= 200; launcher.manager.update("fixed"); } @@ -912,7 +989,16 @@ bool snakeLoop() launcher.manager.end(); - //snake.drawMap(); - return true; +} + +DemoCallbacks getSnakeDemo() +{ + DemoCallbacks demo; + demo.register = &snakeRegister; + demo.initialize = &snakeStart; + demo.deinitialize = &snakeEnd; + demo.loop = &snakeLoop; + demo.tips = snake.tips; + return demo; } \ No newline at end of file diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index 5615f0d..ece3c2b 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -4,31 +4,32 @@ import app; import bindbc.sdl; -import cimgui.cimgui; - import bubel.ecs.attributes; import bubel.ecs.core; import bubel.ecs.entity; import bubel.ecs.manager; import bubel.ecs.std; +import cimgui.cimgui; + import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +import game_core.basic; +import game_core.rendering; +import game_core.collision; + //import std.math : PI; -enum PI = 3.141592653589793238462643383279502884197169399375105820; //import std.array : staticArray; -enum float px = 1.0/512.0; +private enum float px = 1.0/512.0; extern(C): -enum ShootGridDependency = "ShootGridDependency"; - /*####################################################################################################################### ------------------------------------------------ Types ------------------------------------------------------------------ #######################################################################################################################*/ @@ -55,7 +56,7 @@ struct SpaceInvaders ~this() @nogc nothrow { - if(shoot_grid)Mallocator.dispose(shoot_grid); + // if(shoot_grid)Mallocator.dispose(shoot_grid); if(enemy_tmpl)launcher.manager.freeTemplate(enemy_tmpl); if(ship_tmpl)launcher.manager.freeTemplate(ship_tmpl); if(laser_tmpl)launcher.manager.freeTemplate(laser_tmpl); @@ -63,7 +64,7 @@ struct SpaceInvaders { if(tmpl)launcher.manager.freeTemplate(tmpl); } - texture.destory(); + texture.destroy(); } } @@ -89,7 +90,7 @@ struct SceneGrid cells = Mallocator.makeArray!Cell(cells_count.x * cells_count.y); } - void destory() + void destroy() { if(cells) { @@ -114,7 +115,7 @@ enum Direction : byte ------------------------------------------------ Components ------------------------------------------------------------------ #######################################################################################################################*/ -struct CLocation +/*struct CLocation { mixin ECS.Component; @@ -133,13 +134,13 @@ struct CScale vec2 value = vec2(16,16); } -struct CColliderScale +struct CDepth { mixin ECS.Component; - alias value this; + alias depth this; - vec2 value = vec2(16,16); + short depth; } struct CRotation @@ -159,21 +160,16 @@ struct CTexture //Texture tex; uint id; vec4 coords = vec4(0,0,0,1); -} +}*/ -struct CVelocity -{ - mixin ECS.Component; +// struct CVelocity +// { +// mixin ECS.Component; - alias value this; +// alias value this; - vec2 value = vec2(0,0); -} - -struct CInput -{ - mixin ECS.Component; -} +// vec2 value = vec2(0,0); +// } struct CEnemy { @@ -254,26 +250,12 @@ struct CSideMove byte group = -1; } -struct CDepth -{ - mixin ECS.Component; - - alias depth this; - - short depth; -} - -struct CShootGrid -{ - mixin ECS.Component; -} - struct CTargetParent { mixin ECS.Component; EntityID parent; - vec2 rel_pos; + vec2 rel_pos = vec2(0,0); } @@ -334,14 +316,7 @@ struct CAnimationLooped mixin ECS.Component; } -struct CDamping -{ - mixin ECS.Component; - alias value this; - - byte value = 0; -} struct CParticle { @@ -399,8 +374,8 @@ struct CParticleEmitter { mixin ECS.Component; - vec2 range; - vec2 time_range; + vec2 range = vec2(0,0); + vec2 time_range = vec2(500,1000); ///due to multithreading there should be separate template for every thread. ///It can be array of tempaltes or (like in this demo) simply index of template; uint tmpl_id; @@ -512,163 +487,6 @@ struct EDestroyedChild ------------------------------------------------ Systems ------------------------------------------------------------------ #######################################################################################################################*/ -struct ShootGrid -{ - - ~this() @nogc nothrow - { - if(nodes)Mallocator.dispose(nodes); - if(masks)Mallocator.dispose(masks); - } - - struct Node - { - alias entity this; - - EntityID entity; - } - - void create(ivec2 nodes_count, vec2 node_size) - { - this.size = nodes_count; - this.node_size = node_size; - inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y); - nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y); - masks = Mallocator.makeArray!ubyte(nodes.length); - } - - void mark(EntityID id, vec2 beg, vec2 end, byte mask) - { - ivec2 ibeg = cast(ivec2)(beg * inv_node_size); - ivec2 iend = cast(ivec2)((end * inv_node_size) + 0.5); - if(ibeg.x < 0)ibeg.x = 0; - if(ibeg.y < 0)ibeg.y = 0; - if(iend.x > size.x)iend.x = size.x; - if(iend.y > size.y)iend.y = size.y; - foreach(i; ibeg.y .. iend.y) - { - foreach(j; ibeg.x .. iend.x) - { - nodes[i * size.x + j] = id; - masks[i * size.x +j] = mask; - } - } - } - - void clear() - { - size_t size = nodes.length * EntityID.sizeof; - memset(nodes.ptr, 0, size); - memset(masks.ptr, 0, masks.length); - } - - bool test(out EntityID id, vec2 beg, vec2 end) - { - ivec2 ibeg = cast(ivec2)(beg * inv_node_size); - ivec2 iend = cast(ivec2)((end * inv_node_size) + 0.5); - if(ibeg.x < 0)ibeg.x = 0; - if(ibeg.y < 0)ibeg.y = 0; - if(iend.x > size.x)iend.x = size.x; - if(iend.y > size.y)iend.y = size.y; - foreach(i; ibeg.y .. iend.y) - { - foreach(j; ibeg.x .. iend.x) - { - if(nodes[i * size.x + j].id != 0) - { - id = nodes[i * size.x + j]; - return true; - } - } - } - return false; - } - - bool test(out EntityID id, vec2 pos, ubyte mask) - { - ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5); - if(ipos.x < 0)ipos.x = 0; - if(ipos.y < 0)ipos.y = 0; - if(ipos.x >= size.x)ipos.x = size.x - 1; - if(ipos.y >= size.y)ipos.y = size.y - 1; - size_t index = ipos.y * size.x + ipos.x; - if((masks[index] & mask) == 0)return false; - if(nodes[index].id != 0) - { - id = nodes[index]; - return true; - } - return false; - } - - vec2 inv_node_size; - ivec2 size; - vec2 node_size; - Node[] nodes; - ubyte[] masks; -} - -struct ShootGridCleaner -{ - mixin ECS.System!1; - - struct EntitiesData - { - - } - - void onUpdate(EntitiesData data) - { - if(space_invaders.shoot_grid)space_invaders.shoot_grid.clear(); - } -} - -struct ShootGridManager -{ - mixin ECS.System!128; - - mixin ECS.WritableDependencies!(ShootGridDependency); - - struct EntitiesData - { - uint length; - //uint thread_id; - const (Entity)[] entity; - @readonly CLocation[] locations; - @readonly CShootGrid[] grid_flag; - @readonly CGuild[] guild; - @optional @readonly CScale[] scale; - @optional @readonly CColliderScale[] collider_scale; - } - - ShootGrid* grid; - - void onCreate() - { - grid = space_invaders.shoot_grid; - } - - bool onBegin() - { - if(!grid)return false; - //grid.clear(); - return true; - } - - void onUpdate(EntitiesData data) - { - vec2[] scale; - if(data.collider_scale)scale = cast(vec2[])data.collider_scale; - else if(data.scale)scale = cast(vec2[])data.scale; - else return; - foreach(i; 0..data.length) - { - vec2 half_scale = scale[i] * 0.5; - grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, cast(ubyte)(1 << data.guild[i].guild)); - } - } -} - struct ParentOwnerSystem { mixin ECS.System; @@ -699,7 +517,7 @@ struct ShipWeaponSystem Entity[] entity; CInit[] init; //CShip[] ship; - //CChildren[] children; + CChildren[] children; } struct Ship @@ -753,11 +571,11 @@ struct ShipWeaponSystem CDepth* depth = entity.getComponent!CDepth; EntityID[2] weapons; weapon_tmpl.getComponent!CTargetParent().parent = entity.id; - if(depth)weapon_tmpl.getComponent!CDepth().depth = cast(short)(depth.depth - 1); - else weapon_tmpl.getComponent!CDepth().depth = -1; + if(depth)weapon_tmpl.getComponent!CDepth().value = cast(short)(depth.value - 1); + else weapon_tmpl.getComponent!CDepth().value = -1; top_tmpl.getComponent!CTargetParent().parent = entity.id; - if(depth)top_tmpl.getComponent!CDepth().depth = cast(short)(depth.depth - 2); - else top_tmpl.getComponent!CDepth().depth = -2; + if(depth)top_tmpl.getComponent!CDepth().value = cast(short)(depth.value - 2); + else top_tmpl.getComponent!CDepth().value = -2; weapons[0] = launcher.manager.addEntity(weapon_tmpl).id; weapons[1] = launcher.manager.addEntity(top_tmpl).id; @@ -770,7 +588,7 @@ struct ShipWeaponSystem [CWeapon.component_id, CLocation.component_id, CShootDirection.component_id, CTargetParent.component_id, CGuild.component_id, CVelocity.component_id, CAutoShoot.component_id, CTarget.component_id, CTargetPlayerShip.component_id, - CRotation.component_id, CScale.component_id, CTexture.component_id, + CRotation.component_id, CScale.component_id, CTexCoords.component_id, CDepth.component_id, CWeaponLocation.component_id].staticArray); *weapon_tmpl.getComponent!CWeapon = CWeapon(0,CWeapon.Type.laser,3); weapon_tmpl.getComponent!CTargetParent().rel_pos = vec2(0,0); @@ -778,17 +596,17 @@ struct ShipWeaponSystem weapon_tmpl.getComponent!CScale().value = vec2(4,16); //weapon_tmpl.getComponent!CWeapon().level = 1; *weapon_tmpl.getComponent!CWeapon() = CWeapon(0,CWeapon.Type.canon,1); - weapon_tmpl.getComponent!CDepth().depth = -1; - weapon_tmpl.getComponent!CTexture().coords = vec4(136,96,4,16)*px; + weapon_tmpl.getComponent!CDepth().value = -1; + weapon_tmpl.getComponent!CTexCoords().value = vec4(136,96,4,16)*px; weapon_tmpl.getComponent!CWeaponLocation().rel_pos = vec2(0,12); top_tmpl = launcher.manager.allocateTemplate( [CLocation.component_id, CTargetParent.component_id, CScale.component_id, - CTexture.component_id, CDepth.component_id].staticArray); + CTexCoords.component_id, CDepth.component_id].staticArray); top_tmpl.getComponent!CTargetParent().rel_pos = vec2(0,1); top_tmpl.getComponent!CScale().value = vec2(10,11); - top_tmpl.getComponent!CDepth().depth = -2; - top_tmpl.getComponent!CTexture().coords = vec4(112,96,10,11)*px; + top_tmpl.getComponent!CDepth().value = -2; + top_tmpl.getComponent!CTexCoords().value = vec4(112,96,10,11)*px; } @@ -827,22 +645,24 @@ struct ShipWeaponSystem void create() { tower1_tmpl = launcher.manager.allocateTemplate( - [CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, - CTexture.component_id, CScale.component_id, CEnemy.component_id, + [CColor.component_id, CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, + CTexCoords.component_id, CScale.component_id, CEnemy.component_id, CShootGrid.component_id, CGuild.component_id, CInit.component_id, CChildren.component_id, CDepth.component_id, CTargetParent.component_id, - CSpawnUponDeath.component_id, CShootWaveUponDeath.component_id].staticArray + CSpawnUponDeath.component_id, CShootWaveUponDeath.component_id, CShootGridMask.component_id].staticArray ); - CTexture* tex_comp = tower1_tmpl.getComponent!CTexture; + /*CTexCoords* tex_comp = tower1_tmpl.getComponent!CTexCoords; //tex_comp.tex = space_invaders.texture;//ship_tex; tex_comp.coords = vec4(96*px,96*px,16*px,16*px); CLocation* loc_comp = tower1_tmpl.getComponent!CLocation; - loc_comp.value = vec2(64,space_invaders.map_size.y - 16); + loc_comp.value = vec2(64,space_invaders.map_size.y - 16);*/ + tower1_tmpl.getComponent!CTexCoords().value = vec4(96*px,96*px,16*px,16*px); + tower1_tmpl.getComponent!CLocation().value = vec2(64,space_invaders.map_size.y - 16); tower1_tmpl.getComponent!CGuild().guild = 1; tower1_tmpl.getComponent!CInit().type = CInit.Type.tower; tower1_tmpl.getComponent!CHitPoints().value = 10; - tower1_tmpl.getComponent!CDepth().depth = -2; + tower1_tmpl.getComponent!CDepth().value = -2; tower1_tmpl.getComponent!CShootWaveUponDeath().bullet_type = CWeapon.Type.canon; tower1_tmpl.getComponent!CTargetParent().rel_pos = vec2(-33,2); @@ -850,11 +670,11 @@ struct ShipWeaponSystem tower2_tmpl.getComponent!CTargetParent().rel_pos = vec2(33,2); tower3_tmpl = launcher.manager.allocateTemplate(tower1_tmpl); - tower3_tmpl.getComponent!CDepth().depth = 0; + tower3_tmpl.getComponent!CDepth().value = 0; tower3_tmpl.getComponent!CTargetParent().rel_pos = vec2(-40,-15); tower4_tmpl = launcher.manager.allocateTemplate(tower1_tmpl); - tower4_tmpl.getComponent!CDepth().depth = 0; + tower4_tmpl.getComponent!CDepth().value = 0; tower4_tmpl.getComponent!CTargetParent().rel_pos = vec2(40,-15); } @@ -887,6 +707,7 @@ struct ShipWeaponSystem void onDestroy() { + __xdtor(); /*launcher.manager.freeTemplate(laser1_tmpl); launcher.manager.freeTemplate(laser2_tmpl); launcher.manager.freeTemplate(main_weapon_tmpl);*/ @@ -955,7 +776,7 @@ struct MoveToParentTargetSystem } } } - +/* struct DrawSystem { mixin ECS.System!32; @@ -965,7 +786,7 @@ struct DrawSystem uint length; //uint thread_id; uint job_id; - @readonly CTexture[] textures; + @readonly CTexCoords[] textures; @readonly CLocation[] locations; @readonly CScale[] scale; @readonly @optional CRotation[] rotation; @@ -975,31 +796,52 @@ struct DrawSystem void onUpdate(EntitiesData data) { + if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached + import ecs_utils.gfx.renderer; + Renderer.DrawData draw_data; + draw_data.color = 0x80808080; + draw_data.thread_id = data.job_id; + draw_data.texture = space_invaders.texture; + //uint color_mask = 0xFCFCFCFC; + uint const_map = 0x80A08080;//0x80808080; if(!data.depth) { if(data.hit_mark) { foreach(i; 0..data.length) { - uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; - short depth = cast(short)(data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color, 0, 0, 0, data.job_id); + draw_data.color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + draw_data.depth = cast(short)(data.locations[i].y); + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color|const_map, 0, 0, 0, data.job_id); } } else if(data.rotation) { foreach(i; 0..data.length) { - short depth = cast(short)(data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, data.rotation[i], 0, 0, data.job_id); + draw_data.depth = cast(short)(data.locations[i].y); + draw_data.angle = data.rotation[i]; + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080|const_map, data.rotation[i], 0, 0, data.job_id); } } else { foreach(i; 0..data.length) { - short depth = cast(short)(data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, 0, 0, 0, data.job_id); + draw_data.depth = cast(short)(data.locations[i].y); + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080|const_map, 0, 0, 0, data.job_id); } } } @@ -1011,18 +853,27 @@ struct DrawSystem { foreach(i; 0..data.length) { - uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; - short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color, data.rotation[i], 0, 0, data.job_id); + draw_data.color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + draw_data.angle = data.rotation[i]; + draw_data.depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color|const_map, data.rotation[i], 0, 0, data.job_id); } } else { foreach(i; 0..data.length) { - uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; - short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color, 0, 0, 0, data.job_id); + draw_data.color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + draw_data.depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, color|const_map, 0, 0, 0, data.job_id); } } } @@ -1030,22 +881,31 @@ struct DrawSystem { foreach(i; 0..data.length) { - short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, data.rotation[i], 0, 0, data.job_id); + draw_data.angle = data.rotation[i]; + draw_data.depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080|const_map, data.rotation[i], 0, 0, data.job_id); } } else { foreach(i; 0..data.length) { - short depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); - launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080, 0, 0, 0, data.job_id); + draw_data.depth = cast(short)(data.depth[i] * 8 + data.locations[i].y); + draw_data.coords = data.textures[i].value; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + //launcher.renderer.draw(space_invaders.texture, data.locations[i].value, data.scale[i], data.textures[i].coords, depth, 0x80808080|const_map, 0, 0, 0, data.job_id); } } } //if(data.thread_id == 0)launcher.renderer.pushData(); } -} +}*/ struct CollisionSystem { @@ -1062,7 +922,7 @@ struct CollisionSystem } } -struct LaserShootingSystem +struct ShootingSystem { mixin ECS.System!32; @@ -1098,31 +958,31 @@ struct LaserShootingSystem void onCreate() { /*bullet_tmpl[0] = launcher.manager.allocateTemplate( - [CLocation.component_id, CTexture.component_id, CVelocity.component_id, + [CLocation.component_id, CTexCoords.component_id, CVelocity.component_id, CScale.component_id, CBullet.component_id, CGuild.component_id].staticArray ); - bullet_tmpl[0].getComponent!CTexture().coords = vec4(0,24,2,8)*px; + bullet_tmpl[0].getComponent!CTexCoords().value = vec4(0,24,2,8)*px; bullet_tmpl[0].getComponent!CScale().value = vec2(2,8); bullet_tmpl[1] = launcher.manager.allocateTemplate(bullet_tmpl[0]); bullet_tmpl[2] = launcher.manager.allocateTemplate(bullet_tmpl[0]); - bullet_tmpl[2].getComponent!CTexture().coords = vec4(64,32,8,16)*px; + bullet_tmpl[2].getComponent!CTexCoords().value = vec4(64,32,8,16)*px; bullet_tmpl[2].getComponent!CScale().value = vec2(8,16); bullet_tmpl[3] = launcher.manager.allocateTemplate(bullet_tmpl[0]); - bullet_tmpl[3].getComponent!CTexture().coords = vec4(56,32,2,2)*px; + bullet_tmpl[3].getComponent!CTexCoords().value = vec4(56,32,2,2)*px; bullet_tmpl[3].getComponent!CScale().value = vec2(2,2); - // bullet_tmpl[3].getComponent!CTexture().coords = vec4(48,32,8,8)*px; + // bullet_tmpl[3].getComponent!CTexCoords().value = vec4(48,32,8,8)*px; // bullet_tmpl[3].getComponent!CScale().value = vec2(8,8); bullet_tmpl[4] = launcher.manager.allocateTemplate(bullet_tmpl[0]);*/ fire_tmpl = launcher.manager.allocateTemplate( - [CLocation.component_id, CTexture.component_id, CScale.component_id, + [CLocation.component_id, CTexCoords.component_id, CScale.component_id, CAnimation.component_id, CParticle.component_id, CRotation.component_id, CVelocity.component_id, CDamping.component_id].staticArray ); - fire_tmpl.getComponent!CTexture().coords = vec4(96,64,8,16)*px; + fire_tmpl.getComponent!CTexCoords().value = vec4(96,64,8,16)*px; fire_tmpl.getComponent!CScale().value = vec2(8,16); fire_tmpl.getComponent!(CParticle).life = 300; *fire_tmpl.getComponent!(CAnimation) = CAnimation(fire_frames, 0, 3); @@ -1172,8 +1032,8 @@ struct LaserShootingSystem laser.shoot_time -= CWeapon.levels[laser.level - 1].reload_time; laser_location.value = data.location[i]; - laser_velocity.value = vec2((randomf()*2-1) * CWeapon.levels[laser.level - 1].dispersion,1);//data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); - if(data.shoot_direction && data.shoot_direction[i].direction == Direction.down)laser_velocity.y = -1; + laser_velocity.value = vec2((randomf()*2-1) * CWeapon.levels[laser.level - 1].dispersion,0.5);//data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); + if(data.shoot_direction && data.shoot_direction[i].direction == Direction.down)laser_velocity.y = -0.5; laser_guild.guild = data.guild[i].guild; @@ -1187,7 +1047,7 @@ struct LaserShootingSystem else fire_velocity.value = vec2(0,0); fire_location.value = data.location[i]; - if(data.shoot_direction[i].direction == Direction.down) + if(data.shoot_direction && data.shoot_direction[i].direction == Direction.down) { fire_rotation.value = PI; //fire_location.value.y -= 16; @@ -1237,40 +1097,7 @@ struct LaserShootingSystem } } -struct DampingSystem -{ - mixin ECS.System!32; - - struct EntitiesData - { - uint length; - const (Entity)[] entity; - @readonly CDamping[] damping; - CVelocity[] velocity; - } - - float[10] damp = 0; - - bool onBegin() - { - foreach(i;0..10) - { - damp[i] = powf((0.98 - cast(float)i * 0.02),launcher.delta_time*0.1); - } - - return true; - } - - void onUpdate(EntitiesData data) - { - foreach(i; 0..data.length) - { - data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; - } - } -} - -struct LaserCollisionSystem +struct BulletsCollisionSystem { mixin ECS.System!32; @@ -1282,7 +1109,7 @@ struct LaserCollisionSystem uint length; const (Entity)[] entity; @readonly CLocation[] location; - @readonly CBullet[] laser; + @readonly CBullet[] bullet; @readonly CGuild[] guild; } @@ -1293,13 +1120,36 @@ struct LaserCollisionSystem { if(space_invaders.shoot_grid.test(id, data.location[i], cast(ubyte)(~(1 << data.guild[i].guild)))) { - launcher.manager.sendEvent(id, EBulletHit(data.entity[i].id,1)); + launcher.manager.sendEvent(id, EBulletHit(data.entity[i].id,data.bullet[i].damage)); //launcher.manager.removeEntity(data.entity[i].id); } } } } +struct CollisionMaskSystem +{ + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(ShootGridDependency); + + struct EntitiesData + { + ///variable named "length" contain entites count + uint length; + CShootGridMask[] mask; + @readonly CGuild[] guild; + } + + void onAddEntity(EntitiesData data) + { + foreach(i;0..data.length) + { + data.mask[i] = cast(ubyte)(1 << data.guild[i].guild); + } + } +} + struct ParticleEmittingSystem { mixin ECS.System!32; @@ -1324,7 +1174,7 @@ struct ParticleEmittingSystem void onCreate() { templates[0] = launcher.manager.allocateTemplate( - [CLocation.component_id, CTexture.component_id, CScale.component_id, + [CLocation.component_id, CTexCoords.component_id, CScale.component_id, CAnimation.component_id, CParticle.component_id, CRotation.component_id, CVelocity.component_id, CDamping.component_id, CDepth.component_id].staticArray); *templates[0].getComponent!CAnimation() = CAnimation(flashes,0,2); @@ -1360,7 +1210,7 @@ struct ParticleEmittingSystem if(data.depth) { - depth.depth = data.depth[i]; + depth.value = data.depth[i]; } launcher.manager.addEntity(templates[0],[data.location[i].ref_,velocity.ref_,depth.ref_].staticArray); @@ -1392,7 +1242,7 @@ struct UpgradeCollisionSystem if(space_invaders.shoot_grid.test(id, data.location[i], cast(ubyte)(0xFF))) { Entity* entity = launcher.manager.getEntity(id); - if(entity.hasComponent(CShip.component_id)) + if(entity && entity.hasComponent(CShip.component_id)) { launcher.manager.sendEvent(id, EUpgrade()); launcher.manager.removeEntity(data.entity[i].id); @@ -1423,9 +1273,12 @@ struct UpgradeSystem if(ship) { CChildren* children = entity.getComponent!CChildren; - foreach(child;children.childern) + if(children) { - launcher.manager.sendEvent(child,EUpgrade()); + foreach(child;children.childern) + { + launcher.manager.sendEvent(child,EUpgrade()); + } } } } @@ -1585,6 +1438,7 @@ struct HitMarkingSystem { uint length; CHitMark[] mark; + CColor[] color; } void onUpdate(EntitiesData data) @@ -1594,6 +1448,7 @@ struct HitMarkingSystem //if(data.mark[i] < 10)data.mark[i] = 0; //else data.mark[i] -= 1; data.mark[i] = cast(ubyte)(data.mark[i] * 0.9); + data.color[i] = 0x80808080 + 0x01010101 * data.mark[i]; } } } @@ -1616,18 +1471,18 @@ struct HitPointsSystem void onCreate() { upgrade_tmpl = launcher.manager.allocateTemplate( - [CVelocity.component_id, CLocation.component_id, CTexture.component_id, + [CVelocity.component_id, CLocation.component_id, CTexCoords.component_id, CScale.component_id, CUpgrade.component_id, CAnimation.component_id, CAnimationLooped.component_id].staticArray); //tex_comp.tex = space_invaders.texture;//ship_tex; - upgrade_tmpl.getComponent!CTexture().coords = vec4(0*px,32*px,16*px,16*px); + upgrade_tmpl.getComponent!CTexCoords().value = vec4(0*px,32*px,16*px,16*px); *upgrade_tmpl.getComponent!CAnimation = CAnimation(upgrade_laser_frames, 0, 1); - upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.1); + upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.05); explosion_tmpl = launcher.manager.allocateTemplate( [CDepth.component_id, CParticle.component_id, CLocation.component_id, - CTexture.component_id, CScale.component_id, CAnimation.component_id].staticArray); - //explosion_tmpl.getComponent!(CTexture).tex = space_invaders.texture; + CTexCoords.component_id, CScale.component_id, CAnimation.component_id].staticArray); + //explosion_tmpl.getComponent!(CTexCoords).tex = space_invaders.texture; *explosion_tmpl.getComponent!CAnimation = CAnimation(explosion_laser_frames, 0, 1.333); explosion_tmpl.getComponent!(CParticle).life = 600; *explosion_tmpl.getComponent!CDepth = -1; @@ -1742,7 +1597,7 @@ struct ShootWaveSystem CLocation* location = entity.getComponent!CLocation; CGuild* guild = entity.getComponent!CGuild; - //LaserShootingSystem.bullet_tmpl + //ShootingSystem.bullet_tmpl EntityTemplate* tmpl = space_invaders.bullet_tmpl[wave.bullet_type]; foreach(dir;dirs) { @@ -1927,32 +1782,32 @@ struct ClampPositionSystem } } -struct MovementSystem -{ - mixin ECS.System!32; +// struct MovementSystem +// { +// mixin ECS.System!32; - struct EntitiesData - { - uint length; - //read only components can be marked with @readonly attribute or with const expression instead - const (CVelocity)[] velocity; - //components are treated as required by default - CLocation[] locations; - //@optional const (CBullet)[] laser; - const (Entity)[] entities; +// struct EntitiesData +// { +// uint length; +// //read only components can be marked with @readonly attribute or with const expression instead +// const (CVelocity)[] velocity; +// //components are treated as required by default +// CLocation[] locations; +// //@optional const (CBullet)[] laser; +// const (Entity)[] entities; - //@optional CSideMove[] side_move; - } +// //@optional CSideMove[] side_move; +// } - void onUpdate(EntitiesData data) - { - foreach(i;0..data.length) - { - data.locations[i].x += data.velocity[i].x * launcher.delta_time * 0.5; - data.locations[i].y += data.velocity[i].y * launcher.delta_time * 0.5; - } - } -} +// void onUpdate(EntitiesData data) +// { +// foreach(i;0..data.length) +// { +// data.locations[i].x += data.velocity[i].x * launcher.delta_time * 0.5; +// data.locations[i].y += data.velocity[i].y * launcher.delta_time * 0.5; +// } +// } +// } struct AnimationSystem { @@ -1962,7 +1817,8 @@ struct AnimationSystem { uint length; CAnimation[] animation; - CTexture[] texture; + //CTexture[] texture; + CTexCoords[] texcoords; @optional @readonly CAnimationLooped[] looped; } @@ -1977,7 +1833,7 @@ struct AnimationSystem while(cast(uint)data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length; if(cast(uint)(data.animation[i].time) >= data.animation[i].frames.length)assert(0); uint index = cast(uint)(data.animation[i].time); - if(index < data.animation[i].frames.length)data.texture[i].coords = data.animation[i].frames[index]; + if(index < data.animation[i].frames.length)data.texcoords[i].value = data.animation[i].frames[index]; } } else @@ -1987,7 +1843,7 @@ struct AnimationSystem data.animation[i].time += dt * data.animation[i].speed; if(cast(uint)data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time = data.animation[i].frames.length - 0.9; uint index = cast(uint)(data.animation[i].time); - if(index < data.animation[i].frames.length)data.texture[i].coords = data.animation[i].frames[index]; + if(index < data.animation[i].frames.length)data.texcoords[i].value = data.animation[i].frames[index]; } } @@ -2180,126 +2036,29 @@ struct CShipIterator //void handleEvent(Entity* entity, ) }//*/ -extern(C) float sqrtf(float x) @nogc nothrow @system; -extern(C) float acosf(float x) @nogc nothrow @system; -extern(C) float sinf(float x) @nogc nothrow @system; -extern(C) float cosf(float x) @nogc nothrow @system; -extern(C) float powf(float x, float y) @nogc nothrow @system; - -/** -*System is responsible for movement of objects with CInput component. -*In this example every entity has same speed when using movement system. -*/ -struct InputMovementSystem -{ - mixin ECS.System!32; - - vec2 move_vector; - - struct EntitiesData - { - uint length; - //read only components can be marked with @readonly attribute or with const expression instead - const (CInput)[] input; - //components are treated as required by default - //CLocation[] locations; - CVelocity[] velocity; - CTexture[] textures; - } - - /** - *onBegin gives opportunity to check keys once and call update on entities only when - *one key is pressed. - */ - bool onBegin() - { - move_vector = vec2(0,0); - if(launcher.getKeyState(SDL_SCANCODE_W)) - { - move_vector += vec2(0,1); - } - else if(launcher.getKeyState(SDL_SCANCODE_S)) - { - move_vector += vec2(0,-1); - } - if(launcher.getKeyState(SDL_SCANCODE_A)) - { - move_vector += vec2(-1,0); - } - else if(launcher.getKeyState(SDL_SCANCODE_D)) - { - move_vector += vec2(1,0); - } - - if(move_vector.x != 0 ||move_vector.y != 0) - { - move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y); - return true; - } - //don't call system update because no key pressed - return false; - } - - /** - *Update is called multiple times in one "manager.update()" call. - *Number of "onUpdate" calls is count of buffers which must be updated during pass. - *When multithreading is used, number of "onUpdate" calls can be greater due to fact that - *JobSystem can split buffers for better data packing. - */ - void onUpdate(EntitiesData data) - { - /*if(move_vector.x == 0) - { - foreach(i; 0..data.length) - { - data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); - } - //return; - }*/ - //move every entity using movement vector - //if(move_vector.x != 0 || move_vector.y != 0) - foreach(i; 0..data.length) - { - data.velocity[i] += move_vector * launcher.delta_time * 0.005; - if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5; - else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5; - if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5; - else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5; - //data.locations[i].x += move_vector.x * launcher.delta_time * 0.25; - //data.locations[i].y += move_vector.y * launcher.delta_time * 0.25; - //if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px); - //else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); - } - /*else - foreach(i; 0..data.length) - { - data.velocity[i] = vec2(0,0); - }*/ - } -} - /*####################################################################################################################### ------------------------------------------------ Functions ------------------------------------------------------------------ #######################################################################################################################*/ __gshared SpaceInvaders* space_invaders; -void spaceInvadersStart() +void spaceInvadersRegister() { + space_invaders = Mallocator.make!SpaceInvaders; space_invaders.texture.create(); space_invaders.texture.load("assets/textures/atlas.png"); - space_invaders.shoot_grid = Mallocator.make!ShootGrid; - space_invaders.shoot_grid.create(ivec2(80,60), vec2(5,5)); - launcher.manager.beginRegister(); launcher.manager.registerDependency(ShootGridDependency); + registerRenderingModule(launcher.manager); + launcher.manager.registerComponent!CLocation; - launcher.manager.registerComponent!CTexture; + launcher.manager.registerComponent!CTexCoords; + //launcher.manager.registerComponent!CTexture; launcher.manager.registerComponent!CInput; launcher.manager.registerComponent!CShip; launcher.manager.registerComponent!CEnemy; @@ -2335,6 +2094,7 @@ void spaceInvadersStart() launcher.manager.registerComponent!CParticleEmitterTime; launcher.manager.registerComponent!CSpawnUponDeath; launcher.manager.registerComponent!CShootWaveUponDeath; + launcher.manager.registerComponent!CShootGridMask; launcher.manager.registerEvent!EChangeDirection; launcher.manager.registerEvent!EDamage; @@ -2346,11 +2106,12 @@ void spaceInvadersStart() //launcher.manager.registerSystem!MoveSystem(0); launcher.manager.registerSystem!DrawSystem(100); launcher.manager.registerSystem!InputMovementSystem(-100); - launcher.manager.registerSystem!MovementSystem(-99); + //launcher.manager.registerSystem!MovementSystem(-99); + launcher.manager.registerSystem!MoveSystem(-99); launcher.manager.registerSystem!ClampPositionSystem(-90); - launcher.manager.registerSystem!LaserShootingSystem(0); + launcher.manager.registerSystem!ShootingSystem(0); launcher.manager.registerSystem!ChangeDirectionSystem(0); - launcher.manager.registerSystem!LaserCollisionSystem(-70); + launcher.manager.registerSystem!BulletsCollisionSystem(-70); launcher.manager.registerSystem!ShootGridManager(-80); launcher.manager.registerSystem!ShootGridCleaner(-101); launcher.manager.registerSystem!HitPointsSystem(0); @@ -2371,16 +2132,67 @@ void spaceInvadersStart() launcher.manager.registerSystem!ChildDestroySystem(-110); launcher.manager.registerSystem!ShootWaveSystem(-100); //launcher.manager.registerSystem!SpawnUponDeathSystem(-110); + launcher.manager.registerSystem!CollisionMaskSystem(-100); launcher.manager.endRegister(); +} + +void spaceInvadersStart() +{ + + // space_invaders.shoot_grid = Mallocator.make!ShootGrid; + // space_invaders.shoot_grid.create(ivec2(80,60), vec2(5,5)); + + space_invaders.shoot_grid = launcher.manager.getSystem!ShootGridManager().grid; + + DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; + draw_system.default_data.color = 0x80808080; + draw_system.default_data.texture = space_invaders.texture; + + launcher.gui_manager.addComponent(CInput(),"Input"); + launcher.gui_manager.addComponent(CShip(),"Ship"); + launcher.gui_manager.addComponent(CEnemy(),"Enemy"); + launcher.gui_manager.addComponent(CAutoShoot(),"Auto Shoot"); + launcher.gui_manager.addComponent(CWeapon(0, CWeapon.Type.laser),"Weapon (laser)"); + launcher.gui_manager.addComponent(CVelocity(vec2(0,0)),"Velocity (0,0)"); + launcher.gui_manager.addComponent(CBullet(),"Bullet (dmg1)"); + launcher.gui_manager.addComponent(CSideMove(),"Side Move"); + launcher.gui_manager.addComponent(CSideMove(0),"Side Move (g1)"); + launcher.gui_manager.addComponent(CSideMove(1),"Side Move (g2)"); + launcher.gui_manager.addComponent(CShootGrid(),"Shoot Grid"); + launcher.gui_manager.addComponent(CGuild(),"Guild (Player)"); + launcher.gui_manager.addComponent(CGuild(1),"Guild (Enemy)"); + launcher.gui_manager.addComponent(CHitPoints(10),"Hit Points (10)"); + launcher.gui_manager.addComponent(CHitMark(),"Hit Mark"); + launcher.gui_manager.addComponent(CUpgrade(CUpgrade.Upgrade.laser),"Upgrade (laser)"); + launcher.gui_manager.addComponent(CParticle(1000),"Particle (1s)"); + //launcher.gui_manager.addComponent(CMaxHitPoints(),"Max Hit Points"); + launcher.gui_manager.addComponent(CDamping(0),"Damping (0)"); + launcher.gui_manager.addComponent(CDamping(4),"Damping (4)"); + launcher.gui_manager.addComponent(CDamping(8),"Damping (8)"); + launcher.gui_manager.addComponent(CTargetParent(),"Target Parent"); + launcher.gui_manager.addComponent(CTargetPlayerShip(),"Target Player Ship"); + launcher.gui_manager.addComponent(CTarget(),"Target"); + launcher.gui_manager.addComponent(CChildren(),"Children"); + launcher.gui_manager.addComponent(CWeaponLocation(vec2(0,16)),"Weapon Location (0,16)"); + launcher.gui_manager.addComponent(CInit(CInit.Type.space_ship),"Init (Ship)"); + launcher.gui_manager.addComponent(CInit(CInit.Type.boss),"Init (Boss)"); + launcher.gui_manager.addComponent(CInit(CInit.Type.tower),"Init (Tower)"); + launcher.gui_manager.addComponent(CBoss(),"Boss"); + launcher.gui_manager.addComponent(CColliderScale(),"Collider Scale"); + launcher.gui_manager.addComponent(CParticleEmitter(),"Particle Emitter"); + launcher.gui_manager.addComponent(CParticleEmitterTime(),"Particle Emitter Time"); + //launcher.gui_manager.addComponent(CSpawnUponDeath(),"Spawn Upon Death"); + launcher.gui_manager.addComponent(CShootWaveUponDeath(CWeapon.Type.canon),"Wave Upon Death"); launcher.gui_manager.addSystem(DrawSystem.system_id,"Draw System"); launcher.gui_manager.addSystem(InputMovementSystem.system_id,"Input Movement"); - launcher.gui_manager.addSystem(LaserShootingSystem.system_id,"Laser Shooting"); - launcher.gui_manager.addSystem(MovementSystem.system_id,"Movement System"); + launcher.gui_manager.addSystem(ShootingSystem.system_id,"Shooting System"); + //launcher.gui_manager.addSystem(MovementSystem.system_id,"Movement System"); + launcher.gui_manager.addSystem(MoveSystem.system_id,"Move System"); launcher.gui_manager.addSystem(ClampPositionSystem.system_id,"Clamp Position System"); launcher.gui_manager.addSystem(ChangeDirectionSystem.system_id,"Change Direction System"); - launcher.gui_manager.addSystem(LaserCollisionSystem.system_id,"Laser Collision System"); + launcher.gui_manager.addSystem(BulletsCollisionSystem.system_id,"Bullets Collision System"); launcher.gui_manager.addSystem(ShootGridManager.system_id,"Shoot Grid Manager"); launcher.gui_manager.addSystem(ShootGridCleaner.system_id,"Shoot Grid Cleaner"); launcher.gui_manager.addSystem(HitPointsSystem.system_id,"Hit Points System"); @@ -2404,29 +2216,31 @@ void spaceInvadersStart() //launcher.manager.getSystem(CleanSystem.system_id).disable(); { space_invaders.ship_tmpl = launcher.manager.allocateTemplate( - [CVelocity.component_id, CHitMark.component_id, CHitPoints.component_id, - CLocation.component_id, CTexture.component_id, CInput.component_id, + [CVelocity.component_id, CColor.component_id, CHitMark.component_id, CHitPoints.component_id, + CLocation.component_id, CTexCoords.component_id, CInput.component_id, CShip.component_id, CScale.component_id, CColliderScale.component_id, CShootDirection.component_id, CShootGrid.component_id, CGuild.component_id, - CDamping.component_id, CChildren.component_id, CInit.component_id].staticArray + CDamping.component_id, CChildren.component_id, CInit.component_id, + CShootGridMask.component_id, CVelocityFactor.component_id].staticArray ); - space_invaders.ship_tmpl.getComponent!CTexture().coords = vec4(0,80,48,32)*px; + space_invaders.ship_tmpl.getComponent!CTexCoords().value = vec4(0,80,48,32)*px; space_invaders.ship_tmpl.getComponent!CScale().value = vec2(48,32); space_invaders.ship_tmpl.getComponent!CHitPoints().value = 1000; - space_invaders.ship_tmpl.getComponent!CDamping().value = 7; + space_invaders.ship_tmpl.getComponent!CDamping().value = 14; space_invaders.ship_tmpl.getComponent!CInit().type = CInit.Type.space_ship; space_invaders.ship_tmpl.getComponent!CColliderScale().value = vec2(26,24); + space_invaders.ship_tmpl.getComponent!CVelocityFactor().value = vec2(0.5,0.5); launcher.manager.addEntity(space_invaders.ship_tmpl,[CLocation(vec2(64,64)).ref_].staticArray); } { - ushort[6] components = [CLocation.component_id, CTexture.component_id, CVelocity.component_id, CScale.component_id, CBullet.component_id, CGuild.component_id]; + ushort[6] components = [CLocation.component_id, CTexCoords.component_id, CVelocity.component_id, CScale.component_id, CBullet.component_id, CGuild.component_id]; space_invaders.laser_tmpl = launcher.manager.allocateTemplate(components); - space_invaders.laser_tmpl.getComponent!CTexture().coords = vec4(0,24,2,8)*px; + space_invaders.laser_tmpl.getComponent!CTexCoords().value = vec4(0,24,2,8)*px; space_invaders.laser_tmpl.getComponent!CScale().value = vec2(2,8); - space_invaders.laser_tmpl.getComponent!CVelocity().value = vec2(0,1); + space_invaders.laser_tmpl.getComponent!CVelocity().value = vec2(0,0.5); } EntityTemplate* enemy_tmpl; @@ -2439,8 +2253,8 @@ void spaceInvadersStart() { boss_tmpl = launcher.manager.allocateTemplate( - [CHitMark.component_id, CParts.component_id, CLocation.component_id, - CTexture.component_id, CScale.component_id, CEnemy.component_id, + [CColor.component_id, CHitMark.component_id, CParts.component_id, CLocation.component_id, + CTexCoords.component_id, CScale.component_id, CEnemy.component_id, CBoss.component_id, CGuild.component_id, CInit.component_id, CChildren.component_id, CSideMove.component_id, CVelocity.component_id, CDepth.component_id].staticArray @@ -2451,24 +2265,24 @@ void spaceInvadersStart() //tex_comp.coords = vec4(128*px,0*px,96*px,48*px); //CLocation* loc_comp = boss_tmpl.getComponent!CLocation; //loc_comp.value = vec2(64,space_invaders.map_size.y - 16); - boss_tmpl.getComponent!CTexture().coords = vec4(128,0,96,48)*px; + boss_tmpl.getComponent!CTexCoords().value = vec4(128,0,96,48)*px; boss_tmpl.getComponent!CGuild().guild = 1; boss_tmpl.getComponent!CInit().type = CInit.Type.boss; boss_tmpl.getComponent!CScale().value = vec2(96,48); - boss_tmpl.getComponent!CDepth().depth = -1; + boss_tmpl.getComponent!CDepth().value = -1; boss_tmpl.getComponent!CParts().count = 4; - boss_tmpl.getComponent!CVelocity().value = vec2(0.05,0); + boss_tmpl.getComponent!CVelocity().value = vec2(0.025,0); } { tower_tmpl = launcher.manager.allocateTemplate( - [CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, - CTexture.component_id, CScale.component_id, CEnemy.component_id, + [CColor.component_id, CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, + CTexCoords.component_id, CScale.component_id, CEnemy.component_id, CShootGrid.component_id, CGuild.component_id, CInit.component_id, - CChildren.component_id].staticArray + CChildren.component_id, CShootGridMask.component_id].staticArray ); - tower_tmpl.getComponent!CTexture().coords = vec4(96,96,16,16)*px; + tower_tmpl.getComponent!CTexCoords().value = vec4(96,96,16,16)*px; tower_tmpl.getComponent!CGuild().guild = 1; tower_tmpl.getComponent!CInit().type = CInit.Type.tower; tower_tmpl.getComponent!CHitPoints().value = 10; @@ -2476,16 +2290,16 @@ void spaceInvadersStart() { space_invaders.enemy_tmpl = launcher.manager.allocateTemplate( - [CWeaponLocation.component_id, CHitMark.component_id, CHitPoints.component_id, + [CWeaponLocation.component_id, CColor.component_id, CHitMark.component_id, CHitPoints.component_id, CVelocity.component_id, CAutoShoot.component_id, CLocation.component_id, - CTexture.component_id, CScale.component_id, CWeapon.component_id, + CTexCoords.component_id, CScale.component_id, CWeapon.component_id, CEnemy.component_id, CShootDirection.component_id, CShootGrid.component_id, - CGuild.component_id].staticArray + CGuild.component_id, CShootGridMask.component_id].staticArray ); - space_invaders.enemy_tmpl.getComponent!CTexture().coords = vec4(32,32,16,16)*px; + space_invaders.enemy_tmpl.getComponent!CTexCoords().value = vec4(32,32,16,16)*px; space_invaders.enemy_tmpl.getComponent!CShootDirection().direction = Direction.down; - space_invaders.enemy_tmpl.getComponent!CVelocity().value = vec2(0.1,0); + space_invaders.enemy_tmpl.getComponent!CVelocity().value = vec2(0.05,0); space_invaders.enemy_tmpl.getComponent!CGuild().guild = 1; space_invaders.enemy_tmpl.getComponent!CWeaponLocation().rel_pos = vec2(0,-15); @@ -2515,9 +2329,9 @@ void spaceInvadersStart() EntityTemplate* upgrade_tmpl; { - upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CUpgrade.component_id, CAnimationLooped.component_id, CAnimation.component_id].staticArray); - upgrade_tmpl.getComponent!CTexture().coords = vec4(0,32,16,16)*px; - upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.1); + upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexCoords.component_id, CScale.component_id, CUpgrade.component_id, CAnimationLooped.component_id, CAnimation.component_id].staticArray); + upgrade_tmpl.getComponent!CTexCoords().value = vec4(0,32,16,16)*px; + upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.05); *upgrade_tmpl.getComponent!CAnimation = CAnimation(HitPointsSystem.upgrade_laser_frames, 0, 0.75); } @@ -2527,20 +2341,20 @@ void spaceInvadersStart() grouped_tmpl = launcher.manager.allocateTemplate(grouped_id); space_invaders.bullet_tmpl[0] = launcher.manager.allocateTemplate( - [CLocation.component_id, CTexture.component_id, CVelocity.component_id, + [CLocation.component_id, CTexCoords.component_id, CVelocity.component_id, CScale.component_id, CBullet.component_id, CGuild.component_id].staticArray ); - space_invaders.bullet_tmpl[0].getComponent!CTexture().coords = vec4(0,24,2,8)*px; + space_invaders.bullet_tmpl[0].getComponent!CTexCoords().value = vec4(0,24,2,8)*px; space_invaders.bullet_tmpl[0].getComponent!CScale().value = vec2(2,8); space_invaders.bullet_tmpl[1] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); space_invaders.bullet_tmpl[2] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); - space_invaders.bullet_tmpl[2].getComponent!CTexture().coords = vec4(64,32,8,16)*px; + space_invaders.bullet_tmpl[2].getComponent!CTexCoords().value = vec4(64,32,8,16)*px; space_invaders.bullet_tmpl[2].getComponent!CScale().value = vec2(8,16); space_invaders.bullet_tmpl[3] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); - space_invaders.bullet_tmpl[3].getComponent!CTexture().coords = vec4(56,32,2,2)*px; + space_invaders.bullet_tmpl[3].getComponent!CTexCoords().value = vec4(56,32,2,2)*px; space_invaders.bullet_tmpl[3].getComponent!CScale().value = vec2(2,2); - // bullet_tmpl[3].getComponent!CTexture().coords = vec4(48,32,8,8)*px; + // bullet_tmpl[3].getComponent!CTexCoords().value = vec4(48,32,8,8)*px; // bullet_tmpl[3].getComponent!CScale().value = vec2(8,8); space_invaders.bullet_tmpl[4] = launcher.manager.allocateTemplate(space_invaders.bullet_tmpl[0]); @@ -2560,7 +2374,7 @@ void spaceInvadersEnd() { /*launcher.manager.getSystem(DrawSystem.system_id).disable(); launcher.manager.getSystem(InputMovementSystem.system_id).disable(); - launcher.manager.getSystem(LaserShootingSystem.system_id).disable(); + launcher.manager.getSystem(ShootingSystem.system_id).disable(); launcher.manager.getSystem(MovementSystem.system_id).disable(); launcher.manager.getSystem(ClampPositionSystem.system_id).disable(); launcher.manager.getSystem(ShootGridCleaner.system_id).disable();*/ @@ -2570,35 +2384,6 @@ void spaceInvadersEnd() space_invaders = null; } -void spaceInvadersTool(vec2 position, Tool tool, int size) -{ - switch(tool) - { - case Tool.entity_spawner: - { - EntityTemplate* tmpl = launcher.gui_manager.getSelectedTemplate(); - CLocation* location = tmpl.getComponent!CLocation; - if(location) - { - position.x += (randomf - 0.5) * size; - position.y += (randomf - 0.5) * size; - if(position.y < 16)position.y = 16; - else if(position.y > 299)position.y = 299; - *location = position; - } - CWeapon* laser_weapon = tmpl.getComponent!CWeapon; - if(laser_weapon) - { - laser_weapon.shoot_time = randomf * CWeapon.levels[laser_weapon.level - 1].reload_time; - } - launcher.manager.addEntity(tmpl); - } - break; - default: - break; - } -} - void spaceInvadersEvent(SDL_Event* event) { @@ -2684,4 +2469,15 @@ bool spaceInvadersLoop() }*/ return true; +} + +DemoCallbacks getSpaceInvadersDemo() +{ + DemoCallbacks demo; + demo.register = &spaceInvadersRegister; + demo.initialize = &spaceInvadersStart; + demo.deinitialize = &spaceInvadersEnd; + demo.loop = &spaceInvadersLoop; + demo.tips = space_invaders.tips; + return demo; } \ No newline at end of file diff --git a/demos/source/game_core/basic.d b/demos/source/game_core/basic.d new file mode 100644 index 0000000..b47ba52 --- /dev/null +++ b/demos/source/game_core/basic.d @@ -0,0 +1,255 @@ +module game_core.basic; + +import bubel.ecs.core; +import bubel.ecs.attributes; + +import ecs_utils.math.vector; + +import gui.attributes; + +import ecs_utils.utils; + +import app : launcher; + +import bindbc.sdl; + +struct CLocation +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(0); +} + +struct CScale +{ + mixin ECS.Component; + + alias value this;///use component as it value + + vec2 value = vec2(16,16); +} + +struct CRotation +{ + mixin ECS.Component; + + alias value this;///use component as it value + + float value = 0; +} + +struct CDepth +{ + mixin ECS.Component; + + alias value this; + + short value; +} + +struct CColor +{ + mixin ECS.Component; + + alias value this; + + @GUIColor uint value; +} + +struct CSelected +{ + mixin ECS.Component; + + bool value = false; +} + +struct CInput +{ + mixin ECS.Component; +} + +struct CDamping +{ + mixin ECS.Component; + + alias value this; + + @GUIRange(0,9) byte value = 1; +} + +struct CVelocity +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(0,0); +} + +struct CVelocityFactor +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(1); +} + + +struct DampingSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + @readonly CDamping[] damping; + CVelocity[] velocity; + } + + float[20] damp = 0; + + bool onBegin() + { + foreach(i;0..20) + { + damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1); + } + + return true; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; + } + } +} + +struct MoveSystem +{ + mixin ECS.System!64; + + struct EntitiesData + { + uint length; + CLocation[] location; + @readonly CVelocity[] velocity; + @optional @readonly CVelocityFactor[] vel_factor; + } + + void onUpdate(EntitiesData data) + { + if(data.vel_factor) + { + foreach(i; 0..data.length) + { + data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.delta_time; + } + } + else + { + foreach(i; 0..data.length) + { + data.location[i] += data.velocity[i] * launcher.delta_time; + } + } + } +} + +/** +*System is responsible for movement of objects with CInput component. +*In this example every entity has same speed when using movement system. +*/ +struct InputMovementSystem +{ + mixin ECS.System!32; + + vec2 move_vector; + + struct EntitiesData + { + uint length; + //read only components can be marked with @readonly attribute or with const expression instead + const (CInput)[] input; + //components are treated as required by default + //CLocation[] locations; + CVelocity[] velocity; + //CTexture[] textures; + } + + /** + *onBegin gives opportunity to check keys once and call update on entities only when + *one key is pressed. + */ + bool onBegin() + { + move_vector = vec2(0,0); + if(launcher.getKeyState(SDL_SCANCODE_W)) + { + move_vector += vec2(0,1); + } + else if(launcher.getKeyState(SDL_SCANCODE_S)) + { + move_vector += vec2(0,-1); + } + if(launcher.getKeyState(SDL_SCANCODE_A)) + { + move_vector += vec2(-1,0); + } + else if(launcher.getKeyState(SDL_SCANCODE_D)) + { + move_vector += vec2(1,0); + } + + if(move_vector.x != 0 ||move_vector.y != 0) + { + move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y); + return true; + } + //don't call system update because no key pressed + return false; + } + + /** + *Update is called multiple times in one "manager.update()" call. + *Number of "onUpdate" calls is count of buffers which must be updated during pass. + *When multithreading is used, number of "onUpdate" calls can be greater due to fact that + *JobSystem can split buffers for better data packing. + */ + void onUpdate(EntitiesData data) + { + /*if(move_vector.x == 0) + { + foreach(i; 0..data.length) + { + data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); + } + //return; + }*/ + //move every entity using movement vector + //if(move_vector.x != 0 || move_vector.y != 0) + foreach(i; 0..data.length) + { + data.velocity[i] += move_vector * launcher.delta_time * 0.005; + if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5; + else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5; + if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5; + else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5; + //data.locations[i].x += move_vector.x * launcher.delta_time * 0.25; + //data.locations[i].y += move_vector.y * launcher.delta_time * 0.25; + //if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px); + //else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); + } + /*else + foreach(i; 0..data.length) + { + data.velocity[i] = vec2(0,0); + }*/ + } +} \ No newline at end of file diff --git a/demos/source/game_core/collision.d b/demos/source/game_core/collision.d new file mode 100644 index 0000000..794f8d8 --- /dev/null +++ b/demos/source/game_core/collision.d @@ -0,0 +1,1023 @@ +module game_core.collision; + +import bubel.ecs.attributes; +import bubel.ecs.block_allocator; +import bubel.ecs.core; +import bubel.ecs.std; +import bubel.ecs.vector; + +import ecs_utils.math.vector; +import ecs_utils.utils; + +import game_core.basic; + + +void registerCollisionModule(EntityManager* manager) +{ + manager.registerDependency(ShootGridDependency); + manager.registerDependency(BVHDependency); + manager.registerDependency(StaticBVHDependency); + + manager.registerComponent!CShootGrid; + manager.registerComponent!CShootGridMask; + manager.registerComponent!CColliderScale; + manager.registerComponent!CBVH; + manager.registerComponent!CAABB; + manager.registerComponent!CStatic; + + manager.registerSystem!ShootGridManager(-80); + manager.registerSystem!ShootGridCleaner(-101); + manager.registerSystem!BVHBuilder(-80); + manager.registerSystem!StaticBVHBuilder(-80); + //manager.registerSystem!BVHBuilder2(-79); + manager.registerSystem!AABBUpdater(-81); +} + +enum ShootGridDependency = "ShootGridDependency"; +enum BVHDependency = "BVHDependency"; +enum StaticBVHDependency = "StaticBVHDependency"; + +struct CShootGrid +{ + mixin ECS.Component; +} + +struct CBVH +{ + mixin ECS.Component; + + uint index; +} + +struct CStatic +{ + mixin ECS.Component; +} + +struct CAABB +{ + mixin ECS.Component; + + alias bounding this; + + AABB bounding; +} + +struct CShootGridMask +{ + mixin ECS.Component; + + alias value this; + + ubyte value; +} + +struct CColliderScale +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(16,16); +} + + +struct ShootGrid +{ + + ~this() @nogc nothrow + { + if(nodes)Mallocator.dispose(nodes); + if(masks)Mallocator.dispose(masks); + } + + struct Node + { + alias entity this; + + EntityID entity; + } + + void create(ivec2 nodes_count, vec2 node_size) + { + this.size = nodes_count; + this.node_size = node_size; + inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y); + nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y); + masks = Mallocator.makeArray!ubyte(nodes.length); + } + + void mark(EntityID id, vec2 beg, vec2 end, ubyte mask) + { + ivec2 ibeg = cast(ivec2)(beg * inv_node_size); + ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5); + if(ibeg.x < 0)ibeg.x = 0; + if(ibeg.y < 0)ibeg.y = 0; + if(iend.x > size.x)iend.x = size.x; + if(iend.y > size.y)iend.y = size.y; + foreach(i; ibeg.y .. iend.y) + { + foreach(j; ibeg.x .. iend.x) + { + nodes[i * size.x + j] = id; + masks[i * size.x + j] = mask; + } + } + } + + void clear() + { + size_t size = nodes.length * EntityID.sizeof; + memset(nodes.ptr, 0, size); + memset(masks.ptr, 0, masks.length); + } + + bool test(out EntityID id, vec2 beg, vec2 end, ubyte mask) + { + ivec2 ibeg = cast(ivec2)(beg * inv_node_size); + ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5); + if(ibeg.x < 0)ibeg.x = 0; + if(ibeg.y < 0)ibeg.y = 0; + if(iend.x > size.x)iend.x = size.x; + if(iend.y > size.y)iend.y = size.y; + foreach(i; ibeg.y .. iend.y) + { + foreach(j; ibeg.x .. iend.x) + { + uint index = i * size.x + j; + if(nodes[index].id != 0) + { + if((masks[index] & mask) == 0)continue; + id = nodes[index]; + return true; + } + } + } + return false; + } + + bool test(out EntityID id, vec2 pos, ubyte mask) + { + ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5); + if(ipos.x < 0)ipos.x = 0; + if(ipos.y < 0)ipos.y = 0; + if(ipos.x >= size.x)ipos.x = size.x - 1; + if(ipos.y >= size.y)ipos.y = size.y - 1; + size_t index = ipos.y * size.x + ipos.x; + if((masks[index] & mask) == 0)return false; + if(nodes[index].id != 0) + { + id = nodes[index]; + return true; + } + return false; + } + + vec2 inv_node_size; + ivec2 size; + vec2 node_size; + Node[] nodes; + ubyte[] masks; +} + +struct ShootGridCleaner +{ + mixin ECS.System!1; + + struct EntitiesData + { + + } + + ShootGrid* grid; + + bool onBegin() + { + grid = gEM.getSystem!ShootGridManager().grid; + if(grid != null)return true; + else return false; + } + + void onUpdate(EntitiesData data) + { + if(grid)grid.clear(); + } +} + +struct ShootGridManager +{ + mixin ECS.System!128; + + mixin ECS.WritableDependencies!(ShootGridDependency); + + struct EntitiesData + { + uint length; + //uint thread_id; + const (Entity)[] entity; + @readonly CLocation[] locations; + @readonly CShootGrid[] grid_flag; + //@readonly CGuild[] guild; + @optional @readonly CShootGridMask[] mask; + @optional @readonly CScale[] scale; + @optional @readonly CColliderScale[] collider_scale; + } + + ShootGrid* grid; + + void onCreate() + { + //grid = space_invaders.shoot_grid; + grid = Mallocator.make!ShootGrid; + grid.create(ivec2(80,60), vec2(5,5)); + } + + void onDestroy() + { + Mallocator.dispose(grid); + } + + // bool onBegin() + // { + // //if(!grid)return false; + // //grid.clear(); + // return true; + // } + + void onUpdate(EntitiesData data) + { + vec2[] scale; + if(data.collider_scale)scale = cast(vec2[])data.collider_scale; + else if(data.scale)scale = cast(vec2[])data.scale; + else return; + if(data.mask is null) + { + foreach(i; 0..data.length) + { + vec2 half_scale = scale[i] * 0.5; + grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, ubyte.max);//cast(ubyte)(1 << data.guild[i].guild)); + } + } + else foreach(i; 0..data.length) + { + vec2 half_scale = scale[i] * 0.5; + grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, data.mask[i]);//cast(ubyte)(1 << data.guild[i].guild)); + } + + } +} + +struct AABB +{ + vec2 size() + { + return max-min; + } + + vec2 center() + { + return (max+min) * 0.5; + } + + float area() + { + return size.x * size.y; + } + + void set(ref AABB base, vec2 position, float angle, vec2 scale) + { + import std.algorithm.comparison : max; + + float sr = sinf(angle); + float cr = cosf(angle); + /*mat2 m = mat2(cr,-sr, + sr,cr);*/ + + + //vec2 pos = ;//m * ((base.max + base.min)*0.5*scale); + vec2 size = (base.max - base.min)*scale; + vec2[2] axis = [vec2(cr*size.x,sr*size.y),vec2(-sr*size.x,cr*size.y)]; + + this.max.x = max(fabs(axis[0].x),fabs(axis[1].x)); + this.max.y = max(fabs(axis[0].y),fabs(axis[1].y)); + + this.min = -this.max; + + this.min += center + position; + this.max += center + position; + } + + void set(ref AABB base, vec2 position, vec2 scale) + { + vec2 size = (base.max - base.min)*scale; + + this.min = -size; + this.max = size; + + this.min += center + position; + this.max += center + position; + } + + void set(ref AABB base, vec2 position, float angle) + { + import std.algorithm.comparison : max; + + float sr = sinf(angle); + float cr = cosf(angle); + /*mat2 m = mat2(cr,-sr, + sr,cr);*/ + + + //vec2 pos = ;//m * ((base.max + base.min)*0.5*scale); + vec2 size = (base.max - base.min);//*scale; + vec2[2] axis = [vec2(cr*size.x,sr*size.y),vec2(-sr*size.x,cr*size.y)]; + + this.max.x = max(fabs(axis[0].x),fabs(axis[1].x)); + this.max.y = max(fabs(axis[0].y),fabs(axis[1].y)); + + this.min = -this.max; + + this.min += center + position; + this.max += center + position; + } + + void set(ref AABB base, vec2 position) + { + min = base.min + position; + max = base.max + position; + } + + vec2 min; + vec2 max; +} + +bool test(AABB a, AABB b) +{ + if((a.max.x>b.min.x && a.max.y>b.min.y) && + (a.min.x b.max.x && a.max.y > b.max.y)return 2; + else if((a.max.x>b.min.x && a.max.y>b.min.y) && + (a.min.xb.min.x && point.y>b.min.y) && + (point.x coords.length)return vec4(0,0,1,1); + else return coords[index]; + } + + ushort getCoordIndex(vec4 coords) + { + ushort ret = coords_map.get(coords, ushort.max); + if(ret != ushort.max) + { + return ret; + } + this.coords.add(coords); + coords_map.add(coords, cast(ushort)(this.coords.length - 1)); + return cast(ushort)(this.coords.length - 1); + } + + Vector!vec4 coords; + HashMap!(vec4,ushort) coords_map; +} + +struct DrawSystem +{ + mixin ECS.System!32; + + import ecs_utils.gfx.renderer : Renderer; + + struct EntitiesData + { + uint length; + //uint thread_id; + uint job_id; + const(Entity)[] entity; + @readonly CLocation[] locations; + @readonly @optional CScale[] scale; + // @readonly CTexCoords[] texcoord; + @readonly @optional CTexCoords[] texcoord; + @readonly @optional CTexCoordsIndex[] texcoord_index; + @readonly @optional CRotation[] rotation; + @readonly @optional CDepth[] depth; + @readonly @optional CColor[] color; + @readonly @optional CMaterialIndex[] material; + @readonly @optional CDrawDefault[] draw_default; + } + + Renderer.DrawData default_data; + float color_time = 0; + uint select_color = 0; + + bool onBegin() + { + import app : launcher; + color_time += launcher.delta_time * 0.001; + color_time = color_time - cast(int)(color_time*0.5)*2; + float ratio = color_time - cast(int)color_time; + if(color_time > 1)ratio = 1 - ratio; + uint multipler = cast(uint)(0x60 * ratio); + select_color = 0xA0A0A0A0 + cast(uint)(0x01010101 * multipler); + return true; + } + + void onUpdate(EntitiesData data) + { + import app : launcher; + + if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached + Renderer.DrawData draw_data = default_data; + draw_data.thread_id = data.job_id; + + if(launcher.show_filtered && launcher.filterEntity(data.entity[0])) + { + draw_data.color = select_color; + data.color = null; + } + //import std.stdio; + //writeln(data.draw_default); + //if(data.draw_default is null && data.texcoord is null && data.texcoord_index is null && !data.entity[0].hasComponent(CDrawDefault.component_id))return; + + if(data.texcoord is null && data.texcoord_index is null && data.draw_default is null)return; + + + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + if(data.color)draw_data.color = data.color[i]; + if(data.depth)draw_data.depth = data.depth[i]; + if(data.rotation)draw_data.angle = data.rotation[i]; + if(data.scale)draw_data.size = data.scale[i]; + if(data.texcoord)draw_data.coords = data.texcoord[i]; + else if(data.texcoord_index)draw_data.coords = TexCoordsManager.instance.get(data.texcoord_index[i]); + if(data.material)draw_data.material_id = data.material[i]; + launcher.renderer.draw(draw_data); + }//*/ + + /* + ubyte mode; + if(data.scale)mode |= 0x01; + if(data.texcoord)mode |= 0x02; + if(data.texcoord_index)mode |= 0x04; + if(data.rotation)mode |= 0x08; + if(data.depth)mode |= 0x10; + if(data.color)mode |= 0x20; + + if(launcher.show_filtered && launcher.filterEntity(data.entity[0])) + { + draw_data.color = select_color; + mode &= ~0x20; + //goto draw_nocolor; + } + + switch(mode) + { + case 0: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + break; + + case 0b000001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + + case 0b000010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b000011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + + case 0b001000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.angle = data.rotation[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b001001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b001010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b001011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + + + case 0b010000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b010001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b010010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b010011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b011000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b011001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b011010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b011011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + + + case 0b100000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b100001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b100010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b100011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b101000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.angle = data.rotation[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b101001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b101010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b101011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b110000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b110001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b110010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b110011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b111000: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b111001: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b111010: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + case 0b111011: + foreach(i; 0..data.length) + { + draw_data.position = data.locations[i]; + draw_data.color = data.color[i]; + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.size = data.scale[i]; + draw_data.coords = data.texcoord[i]; + launcher.renderer.draw(draw_data); + } + break; + default:break; + }//*/ +/* + if(!data.color) + { + draw_nocolor: + if(!data.depth) + { + if(!data.rotation) + { + foreach(i; 0..data.length) + { + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + else + { + foreach(i; 0..data.length) + { + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + } + else + { + if(!data.rotation) + { + foreach(i; 0..data.length) + { + draw_data.depth = data.depth[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + else + { + foreach(i; 0..data.length) + { + draw_data.depth = data.depth[i]; + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + } + } + else + { + if(!data.depth) + { + if(!data.rotation) + { + foreach(i; 0..data.length) + { + draw_data.color = data.color[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + else + { + foreach(i; 0..data.length) + { + draw_data.color = data.color[i]; + draw_data.angle = data.rotation[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + } + else + { + if(!data.rotation) + { + foreach(i; 0..data.length) + { + draw_data.depth = data.depth[i]; + draw_data.color = data.color[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + else + { + foreach(i; 0..data.length) + { + draw_data.depth = data.depth[i]; + draw_data.color = data.color[i]; + draw_data.coords = data.texcoord[i]; + draw_data.size = data.scale[i]; + draw_data.position = data.locations[i]; + launcher.renderer.draw(draw_data); + } + } + } + }*/ + } +} \ No newline at end of file diff --git a/demos/source/gui/attributes.d b/demos/source/gui/attributes.d new file mode 100644 index 0000000..53605c2 --- /dev/null +++ b/demos/source/gui/attributes.d @@ -0,0 +1,20 @@ +module gui.attributes; + +enum GUIColor = "GUIColor"; + +struct GUIRange +{ + union + { + struct + { + int min; + int max; + } + struct + { + float minf; + float maxf; + } + } +} \ No newline at end of file diff --git a/demos/source/gui/component.d b/demos/source/gui/component.d index bbd7ec5..e65dee4 100644 --- a/demos/source/gui/component.d +++ b/demos/source/gui/component.d @@ -1,6 +1,63 @@ -module gui.components; +module gui.component; + +import ecs_utils.utils; struct ComponentGUI { - -} \ No newline at end of file + const (char)* name; + void* data; + ushort component_id; +} + +struct ComponentEditGUI +{ + const (char)* name; + VariableGUI[] variables; + uint used; +} + +struct VariableGUI +{ + struct Int + { + int min; + int max; + } + + struct Float + { + float min; + float max; + } + + struct Enum + { + const (char)[][] strings; + } + + enum Type + { + byte_, + ubyte_, + short_, + ushort_, + int_, + uint_, + float_, + enum_, + color, + vec2, + ivec2, + vec4, + ivec4 + } + Type type; + const (char)* name; + ushort offset; + union + { + Int int_; + Float float_; + Enum enum_; + } +} diff --git a/demos/source/gui/manager.d b/demos/source/gui/manager.d index 4d452ad..c7be12f 100644 --- a/demos/source/gui/manager.d +++ b/demos/source/gui/manager.d @@ -4,22 +4,55 @@ import app; import cimgui.cimgui; +import bubel.ecs.entity; +import bubel.ecs.manager; import bubel.ecs.std; import bubel.ecs.system; import bubel.ecs.vector; -import bubel.ecs.entity; +import ecs_utils.math.vector; + +import gui.attributes; +import gui.component; import gui.system; import gui.template_; +import std.traits; + extern(C): struct GUIManager { Vector!SystemGUI systems; + Vector!ComponentGUI components; Vector!TemplateGUI templates; + Vector!ComponentEditGUI edit_components; + Vector!bool filter; + Vector!ushort filter_list; - uint selected_tempalte = 0; + int selected_template = 0; + int selected_component = 0; + + ~this() + { + clear(); + } + + void selectTemplate(int id) + { + if(templates.length == 0)return; + selected_template = id; + while(selected_template < 0)selected_template += cast(int)templates.length; + while(selected_template >= templates.length)selected_template -= cast(int)templates.length; + } + + void selectComponent(int id) + { + if(components.length == 0)return; + selected_component = id; + while(selected_component < 0)selected_component += cast(int)components.length; + while(selected_component >= components.length)selected_component -= cast(int)components.length; + } void clear() { @@ -27,23 +60,51 @@ struct GUIManager { launcher.manager.freeTemplate(tmpl.tmpl); } + foreach(comp; components) + { + free(comp.data); + } + foreach(ref comp; edit_components) + { + if(comp.variables)Mallocator.dispose(comp.variables); + comp.variables = null; + comp.used = 0; + comp.name = null; + } + foreach(ref comp; filter) + { + comp = false; + } + filter_list.clear(); systems.clear(); templates.clear(); - selected_tempalte = 0; + components.clear(); + selected_template = 0; + selected_component = 0; } EntityTemplate* getSelectedTemplate() { - if(templates.length > selected_tempalte)return templates[selected_tempalte].tmpl; + if(templates.length > selected_template)return templates[selected_template].tmpl; else return null; } + ComponentRef getSelectedComponent() + { + if(components.length > selected_component)return ComponentRef(components[selected_component].data, components[selected_component].component_id); + else return ComponentRef(null, ushort.max); + } + void addSystem(ushort id, const (char)* name, bool enabled = true) { + foreach(ref sys; systems) + { + if(sys.id == id)return; + } System* system = launcher.manager.getSystem(id); //const (char)* name = - systems.add(SystemGUI(name,system,enabled)); + systems.add(SystemGUI(name,id,enabled)); } void addTemplate(ushort[] components, const (char)* name) @@ -56,43 +117,394 @@ struct GUIManager templates.add(TemplateGUI(name, tmpl)); } + // void addComponent(ComponentRef comp, const (char)* name) + // { + // uint size = EntityManager.instance.components[comp.component_id].size; + // void* data = malloc(size); + // memcpy(data, comp.ptr, size); + // components.add(ComponentGUI(name, data, comp.component_id)); + // } + + void addComponent(T)(T comp, const (char)* name) + { + static assert(hasStaticMember!(T,"component_id")); + uint size = EntityManager.instance.components[comp.component_id].size; + void* data = malloc(size); + memcpy(data, &comp, size); + components.add(ComponentGUI(name, data, comp.component_id)); + + if(edit_components.length <= comp.component_id) + { + edit_components.length = comp.component_id+1;//.extend(comp.component_id + 1); + } + //edit_components[comp.component_id] = ComponentEditGUI(name); + if(edit_components[comp.component_id].variables)return; + ComponentEditGUI comp_edit; + comp_edit.name = T.stringof; + //enum fields = __traits(allMembers, T); + alias fields = FieldNameTuple!T; + //pragma(msg,fields); + comp_edit.variables = Mallocator.makeArray!VariableGUI(fields.length); + foreach(member_str; fields) + { + alias member = __traits(getMember, T, member_str); + alias member_type = typeof(member); + //pragma(msg,member); + //pragma(msg,member_type); + //pragma(msg,__traits(getMember, T, member).offsetof); + ushort offset = member.offsetof;//cast(ushort)__traits(getMember, T, member).offsetof; + static if(is(member_type == vec2)) + { + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.vec2,member_str,offset); + } + else static if(is(member_type == ivec2)) + { + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ivec2,member_str,offset); + } + else static if(is(member_type == vec4)) + { + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.vec4,member_str,offset); + } + else static if(is(member_type == ivec4)) + { + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ivec4,member_str,offset); + } + else static if(__traits(isIntegral,member_type)) + { + static if(__traits(isUnsigned, member_type)) + { + static if(hasUDA!(member,GUIColor)) + { + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.color,member_str,offset); + } + else switch(member_type.sizeof) + { + case 1: comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ubyte_,member_str,offset);break; + case 2: comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ushort_,member_str,offset);break; + case 4: comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.uint_,member_str,offset);break; + default:break; + } + static if(hasUDA!(member,GUIRange)) + { + comp_edit.variables[comp_edit.used-1].int_.min = getUDAs!(member,GUIRange)[0].min; + comp_edit.variables[comp_edit.used-1].int_.max = getUDAs!(member,GUIRange)[0].max; + } + else + { + comp_edit.variables[comp_edit.used-1].int_.min = 0; + comp_edit.variables[comp_edit.used-1].int_.max = int.max; + } + } + else + { + switch(member_type.sizeof) + { + case 1: + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.byte_,member_str,offset); + comp_edit.variables[comp_edit.used-1].int_.min = byte.min; + comp_edit.variables[comp_edit.used-1].int_.max = byte.max; + break; + case 2: + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.short_,member_str,offset); + comp_edit.variables[comp_edit.used-1].int_.min = short.min; + comp_edit.variables[comp_edit.used-1].int_.max = short.max; + break; + case 4: + comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.int_,member_str,offset); + comp_edit.variables[comp_edit.used-1].int_.min = int.min; + comp_edit.variables[comp_edit.used-1].int_.max = int.max; + break; + default:break; + } + static if(hasUDA!(member,GUIRange)) + { + comp_edit.variables[comp_edit.used-1].int_.min = getUDAs!(member,GUIRange)[0].min; + comp_edit.variables[comp_edit.used-1].int_.max = getUDAs!(member,GUIRange)[0].max; + } + /*{ + comp_edit.variables[comp_edit.used-1].int_.min = int.min; + comp_edit.variables[comp_edit.used-1].int_.max = int.max; + }*/ + } + } + else static if(__traits(isScalar,member_type)) + { + switch(member_type.sizeof) + { + case 4:comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.float_,member_str,offset);break; + case 8: + default:break; + } + static if(hasUDA!(member,GUIRange)) + { + comp_edit.variables[comp_edit.used-1].float_.min = getUDAs!(member,GUIRange)[0].minf; + comp_edit.variables[comp_edit.used-1].float_.max = getUDAs!(member,GUIRange)[1].maxf; + } + { + comp_edit.variables[comp_edit.used-1].float_.min = -float.max; + comp_edit.variables[comp_edit.used-1].float_.max = float.max; + } + } + } + edit_components[comp.component_id] = comp_edit; + } + void gui() { - if(igCollapsingHeader("Systems", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_FramePadding)) + if(igCollapsingHeader("Systems", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_DefaultOpen)) { - bool true_ = true; + //bool true_ = true; igIndent(8); foreach(ref SystemGUI system;systems) { if(igCheckbox(system.name,&system.enabled)) { - if(system.enabled)system.system.enable(); - else system.system.disable(); + System* sys = launcher.manager.getSystem(system.id); + if(system.enabled)sys.enable(); + else sys.disable(); } } igUnindent(8); } } - void toolGui() + static vec4 colorUintToVec4(uint color) { - if(templates.length) + // color = *cast(uint*)(comp_ptr+var.offset); + return vec4(cast(float)(color & 0xFF) / 255, + cast(float)(color >> 8 & 0xFF) / 255, + cast(float)(color >> 16 & 0xFF) / 255, + cast(float)(color >> 24 & 0xFF) / 255); + } + + static uint colorVec4ToUint(vec4 color) + { + return cast(uint)(color.x * 255) | + cast(uint)(color.y * 255) << 8 | + cast(uint)(color.z * 255) << 16 | + cast(uint)(color.w * 255) << 24; + } + + static bool igDragScalarClamp(const(char)* label, ImGuiDataType data_type, void* v, float v_speed, const(void)* v_min, const(void)* v_max, const(char)* format, float power) + { + ubyte[8] v_backup;// = *v; + final switch(data_type) { - if(igBeginCombo("Template",templates[selected_tempalte].name,0)) - { - foreach(i, tmpl; templates) - { - if(igSelectable(tmpl.name,false,0,ImVec2(0,0))) - { - selected_tempalte = cast(uint)i; - } - } - igEndCombo(); - } + case ImGuiDataType_S8:memcpy(v_backup.ptr, v, 1);break; + case ImGuiDataType_S16:memcpy(v_backup.ptr, v, 2);break; + case ImGuiDataType_S32:memcpy(v_backup.ptr, v, 4);break; + case ImGuiDataType_U8:memcpy(v_backup.ptr, v, 1);break; + case ImGuiDataType_U16:memcpy(v_backup.ptr, v, 2);break; + case ImGuiDataType_U32:memcpy(v_backup.ptr, v, 4);break; + case ImGuiDataType_Float:memcpy(v_backup.ptr, v, 4);break; } - else + if (!igDragScalar(label, data_type, v, v_speed, v_min, v_max, format, power)) + return false; + + final switch(data_type) { - if(igBeginCombo("Template",null,0))igEndCombo(); + case ImGuiDataType_S8: + if(*cast(byte*)v < *cast(byte*)v_min)*cast(byte*)v = *cast(byte*)v_min; + else if(*cast(byte*)v > *cast(byte*)v_max)*cast(byte*)v = *cast(byte*)v_max; + return *cast(byte*)v != *cast(byte*)v_backup.ptr; + case ImGuiDataType_S16: + if(*cast(short*)v < *cast(short*)v_min)*cast(short*)v = *cast(short*)v_min; + else if(*cast(short*)v > *cast(short*)v_max)*cast(short*)v = *cast(short*)v_max; + return *cast(short*)v != *cast(short*)v_backup.ptr; + case ImGuiDataType_S32: + if(*cast(int*)v < *cast(int*)v_min)*cast(int*)v = *cast(int*)v_min; + else if(*cast(int*)v > *cast(int*)v_max)*cast(int*)v = *cast(int*)v_max; + return *cast(int*)v != *cast(int*)v_backup.ptr; + case ImGuiDataType_U8: + if(*cast(ubyte*)v < *cast(ubyte*)v_min)*cast(ubyte*)v = *cast(ubyte*)v_min; + else if(*cast(ubyte*)v > *cast(ubyte*)v_max)*cast(ubyte*)v = *cast(ubyte*)v_max; + return *cast(ubyte*)v != *cast(ubyte*)v_backup.ptr; + case ImGuiDataType_U16: + if(*cast(ushort*)v < *cast(ushort*)v_min)*cast(ushort*)v = *cast(ushort*)v_min; + else if(*cast(ushort*)v > *cast(ushort*)v_max)*cast(ushort*)v = *cast(ushort*)v_max; + return *cast(ushort*)v != *cast(ushort*)v_backup.ptr; + case ImGuiDataType_U32: + if(*cast(uint*)v < *cast(uint*)v_min)*cast(uint*)v = *cast(uint*)v_min; + else if(*cast(uint*)v > *cast(uint*)v_max)*cast(uint*)v = *cast(uint*)v_max; + return *cast(uint*)v != *cast(uint*)v_backup.ptr; + case ImGuiDataType_Float: + if(*cast(float*)v < *cast(float*)v_min)*cast(float*)v = *cast(float*)v_min; + else if(*cast(float*)v > *cast(float*)v_max)*cast(float*)v = *cast(float*)v_max; + return *cast(float*)v != *cast(float*)v_backup.ptr; } } + + void componentGUI(ushort comp_id, void* data_ptr) + { + vec4 color; + if(comp_id >= edit_components.length)return; + if(edit_components[comp_id].used) + { + if(igCollapsingHeader(edit_components[comp_id].name, ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) + { + igIndent(8); + foreach(ref VariableGUI var;edit_components[comp_id].variables[0 .. edit_components[comp_id].used]) + { + igPushIDPtr(&var); + switch(var.type) + { + case VariableGUI.Type.byte_: + igDragScalarClamp(var.name, ImGuiDataType_S8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); + break; + case VariableGUI.Type.ubyte_: + igDragScalarClamp(var.name, ImGuiDataType_U8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); + break; + case VariableGUI.Type.short_: + igDragScalarClamp(var.name, ImGuiDataType_S16, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); + break; + case VariableGUI.Type.ushort_: + igDragScalarClamp(var.name, ImGuiDataType_U16, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); + break; + case VariableGUI.Type.int_: + igDragScalarClamp(var.name, ImGuiDataType_S32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); + break; + case VariableGUI.Type.uint_: + igDragScalarClamp(var.name, ImGuiDataType_U32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); + break; + case VariableGUI.Type.float_: + igDragScalarClamp(var.name, ImGuiDataType_Float, data_ptr+var.offset, 0.1, cast(void*)&var.float_.min, cast(void*)&var.float_.max, "%2.2f", 1); + break; + case VariableGUI.Type.color: + color = colorUintToVec4(*cast(uint*)(data_ptr+var.offset)); + if(igColorEdit4(var.name, color.data, ImGuiColorEditFlags_None)) + *cast(uint*)(data_ptr+var.offset) = colorVec4ToUint(color); + break; + case VariableGUI.Type.vec2: + igDragFloat2(var.name, (cast(float*)(data_ptr+var.offset))[0..2], 0.1, -float.max, float.max, null, 1); + break; + case VariableGUI.Type.ivec2: + igDragInt2(var.name, (cast(int*)(data_ptr+var.offset))[0..2], 0.1, int.min, int.max, null); + break; + case VariableGUI.Type.vec4: + igDragFloat4(var.name, (cast(float*)(data_ptr+var.offset))[0..4], 0.1, -float.max, float.max, null, 1); + break; + case VariableGUI.Type.ivec4: + igDragInt4(var.name, (cast(int*)(data_ptr+var.offset))[0..4], 0.1, int.min, int.max, null); + break; + default:break; + } + igPopID(); + } + //igPopID(); + igUnindent(8); + } + } + } + + void entityComponentsGUI() + { + if(selected_template >= templates.length)return; + EntityTemplate* tmpl = templates[selected_template].tmpl; + EntityManager.EntityInfo* info = tmpl.info; + foreach(comp_id; info.components) + { + void* data_ptr = tmpl.entity_data.ptr; + void* comp_ptr = data_ptr + info.tmpl_deltas[comp_id]; + componentGUI(comp_id, comp_ptr); + } + } + + void toolGui() + { + ImGuiStyle * style = igGetStyle(); + ImVec4 col = style.Colors[ImGuiCol_Header]; + style.Colors[ImGuiCol_Header] = style.Colors[ImGuiCol_TextSelectedBg]; + //style. + //ImDrawList* draw_list = igGetWindowDrawList(); + final switch(launcher.used_tool) + { + case Tool.entity_spawner: + if(templates.length) + { + { + if(igListBoxHeaderInt("Template",cast(int)templates.length,cast(int)templates.length)) + { + foreach(i, tmpl; templates) + { + if(igSelectable(tmpl.name,selected_template == i,ImGuiSelectableFlags_AllowDoubleClick,ImVec2(0,0))) + { + selected_template = cast(uint)i; + } + } + igListBoxFooter(); + } + } + if(igIsItemHovered(0))igSetTooltip("Select entity to spawn (SHIFT + Scroll)"); + } + style.Colors[ImGuiCol_Header] = col; + entityComponentsGUI(); + break; + case Tool.component_manipulator: + if(components.length) + { + if(igListBoxHeaderInt("Components",cast(int)components.length,cast(int)components.length)) + { + foreach(i, comp; components) + { + if(igSelectable(comp.name,selected_component == i,0,ImVec2(0,0))) + { + selected_component = cast(uint)i; + } + } + igListBoxFooter(); + } + if(igIsItemHovered(0))igSetTooltip("Select component to add/remove (SHIFT + Scroll)"); + } + style.Colors[ImGuiCol_Header] = col; + if(selected_component < components.length)componentGUI(components[selected_component].component_id, components[selected_component].data); + break; + case Tool.selector: + break; + } + + style.Colors[ImGuiCol_Header] = col; + } + + private void genFilterList() + { + filter_list.clear(); + foreach(i, comp; filter) + { + if(comp) + { + filter_list.add(cast(ushort)i); + } + } + } + + void filterGUI() + { + ImGuiStyle * style = igGetStyle(); + ImVec4 col = style.Colors[ImGuiCol_Header]; + style.Colors[ImGuiCol_Header] = style.Colors[ImGuiCol_TextSelectedBg]; + + if(filter.length < edit_components.length)filter.length(edit_components.length); + + int length = 0; + foreach(comp; edit_components) + { + if(comp.name !is null)length++; + } + + if(length && igListBoxHeaderInt("Components##FilterComponents",cast(int)length,cast(int)length)) + { + foreach(i, ref comp; edit_components) + { + if(comp.name is null)continue; + if(igSelectableBoolPtr(comp.name,&filter[i],0,ImVec2(0,0))) + { + genFilterList(); + } + } + igListBoxFooter(); + } + if(igIsItemHovered(0))igSetTooltip("Select components to filter while tool work.\nComponents are only changed for filtered entities."); + + style.Colors[ImGuiCol_Header] = col; + } } \ No newline at end of file diff --git a/demos/source/gui/system.d b/demos/source/gui/system.d index 112bed3..b4899fb 100644 --- a/demos/source/gui/system.d +++ b/demos/source/gui/system.d @@ -5,7 +5,8 @@ import bubel.ecs.system; struct SystemGUI { const (char)* name; - System* system; + //System* system; + ushort id; bool enabled = true; } \ No newline at end of file diff --git a/demos/source/gui/tool_circle.d b/demos/source/gui/tool_circle.d new file mode 100644 index 0000000..c8968d4 --- /dev/null +++ b/demos/source/gui/tool_circle.d @@ -0,0 +1,53 @@ +module gui.tool_circle; + +import ecs_utils.gfx.buffer; +import ecs_utils.gfx.shader; +import ecs_utils.gfx.config; +import ecs_utils.gfx.renderer; + +import ecs_utils.math.vector; + +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; + +struct ToolCircle +{ + //Buffer vbo; + //Buffer ibo; + + uint material_id = 1; + uint mesh_id = 0; + + /*/void generate() + { + ushort[] + }*/ + + void draw(Renderer* renderer, vec2 position, float size, float edge = 1) + { + glDisable(GL_DEPTH_TEST); + position = position * renderer.view_size + renderer.view_pos; + vec2 sizes = renderer.view_size * size; + vec2 sizes2 = vec2(edge,0); + + import core.stdc.string; + ubyte[32] uniform_block; + void* ptr = uniform_block.ptr; + *cast(float*)(ptr) = sizes.x; + *cast(float*)(ptr+4) = 0; + *cast(float*)(ptr+8) = 0; + *cast(float*)(ptr+12) = sizes.y; + memcpy(ptr+16,position.data.ptr,8); + memcpy(ptr+24,sizes2.data.ptr,8); + glEnableVertexAttribArray(0); + + GfxConfig.meshes[mesh_id].bind(); + GfxConfig.materials[material_id].bind(); + GfxConfig.materials[material_id].pushBindings(); + GfxConfig.materials[material_id].pushUniforms(uniform_block.ptr); + //glDisable(GL_DEPTH_TEST); + + glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,null); + } +} \ No newline at end of file diff --git a/demos/utils/dub.json b/demos/utils/dub.json index d8ecfb1..5302fd8 100644 --- a/demos/utils/dub.json +++ b/demos/utils/dub.json @@ -15,14 +15,13 @@ "../external/sources" ], "dependencies": { - "bindbc-sdl":"0.13.0", + "bindbc-sdl":"0.19.0", "ecs":{"path":"../../"} }, "versions": [ "BindSDL_Image", "SDL_2010" ], - "configurations" : [ { "name" : "default", diff --git a/demos/utils/source/ecs_utils/gfx/buffer.d b/demos/utils/source/ecs_utils/gfx/buffer.d index b11cb85..76cab50 100644 --- a/demos/utils/source/ecs_utils/gfx/buffer.d +++ b/demos/utils/source/ecs_utils/gfx/buffer.d @@ -2,8 +2,12 @@ module ecs_utils.gfx.buffer; import bubel.ecs.std; -import glad.gl.gl; -import glad.gl.gles2; +//import glad.gl.gl; +//import glad.gl.gles2; + +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; extern(C): @@ -64,13 +68,13 @@ struct Buffer void map(BindTarget target) nothrow { bind(target); - data.map_ptr = glMapBuffer(target,GL_WRITE_ONLY); + version(Android){}else version(WebAssembly){}else data.map_ptr = glMapBuffer(target,GL_WRITE_ONLY); } void map(uint offset, uint size, BindTarget target, uint flags = MapFlagBits.write | MapFlagBits.flush_explict | MapFlagBits.invalidate_buffer) nothrow { bind(target); - data.map_ptr = glMapBufferRange(target,offset,size,flags); + version(Android){}else data.map_ptr = glMapBufferRange(target,offset,size,flags); } void flush(uint offset, uint size, BindTarget target) nothrow diff --git a/demos/utils/source/ecs_utils/gfx/material.d b/demos/utils/source/ecs_utils/gfx/material.d index fbd88b9..0fca5fb 100644 --- a/demos/utils/source/ecs_utils/gfx/material.d +++ b/demos/utils/source/ecs_utils/gfx/material.d @@ -6,7 +6,9 @@ import bubel.ecs.std; import ecs_utils.gfx.shader; -import glad.gl.gl; +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; //import mutils.serializer.json; @@ -76,6 +78,25 @@ struct Material void bind() nothrow { + final switch(data.blend_mode) + { + case BlendMode.mixed: + glDepthMask(0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case BlendMode.opaque: + glDepthMask(1); + glDisable(GL_BLEND); + break; + case BlendMode.additive: + glDepthMask(0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + //glBlendFunc(GL_ONE, GL_ONE); + break; + } + glUseProgram(data.modules[0].gl_handle); } diff --git a/demos/utils/source/ecs_utils/gfx/mesh.d b/demos/utils/source/ecs_utils/gfx/mesh.d index 20d5855..e953778 100644 --- a/demos/utils/source/ecs_utils/gfx/mesh.d +++ b/demos/utils/source/ecs_utils/gfx/mesh.d @@ -6,7 +6,9 @@ import bubel.ecs.std; import ecs_utils.gfx.buffer; -import glad.gl.gl; +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; //import mutils.serializer.json; extern(C): diff --git a/demos/utils/source/ecs_utils/gfx/renderer.d b/demos/utils/source/ecs_utils/gfx/renderer.d index fcd6a88..b2091cf 100644 --- a/demos/utils/source/ecs_utils/gfx/renderer.d +++ b/demos/utils/source/ecs_utils/gfx/renderer.d @@ -12,6 +12,7 @@ import ecs_utils.math.vector; import bubel.ecs.block_allocator; import bubel.ecs.vector; version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; else import glad.gl.gl; version = ver1; @@ -66,8 +67,10 @@ struct Renderer enum max_items = batch_size;//963; byte[] batch_vertices; ushort[] batch_indices; - void* memory; + void* memory = null; uint items = 0; + uint material_id; + uint texture_id; } Mutex* get_block_mutex; @@ -127,11 +130,11 @@ struct Renderer block_stack_mutex.unlock();*/ foreach(ref Thread thread; threads) { - foreach(VertexBlock block; thread.blocks) + foreach(VertexBlock block; thread.filled_blocks) { allocator.freeBlock(block.memory); } - thread.blocks.clear(); + thread.filled_blocks.clear(); } render_blocks = 0; current_block = 0; @@ -143,13 +146,13 @@ struct Renderer { foreach(ref Thread thread; threads) { - foreach(VertexBlock block; thread.blocks) + foreach(VertexBlock block; thread.filled_blocks) { uint items = block.items; if(items + item_id >= MaxObjects)items = MaxObjects - item_id; batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr); batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr); - draw_list.add(DrawCall(item_id,items)); + draw_list.add(DrawCall(block.texture_id,block.material_id,item_id,items)); item_id += items; } //thread.blocks.clear(); @@ -172,17 +175,38 @@ struct Renderer foreach(i, ref Thread thread; threads) { //pushBlock(thread.block); - thread.blocks.add(thread.block); - thread.block = getBlock(); + foreach(ref block; thread.blocks) + { + if(block.items > 0) + { + thread.filled_blocks.add(block); + block.items = 0; + } + } + //thread.blocks = getBlock(); } } + struct DrawData + { + Texture texture; + vec2 position; + vec2 size; + vec4 coords; + short depth = 0; + uint color = uint.max; + float angle = 0; + uint material_id = 0; + uint mesh_id = 0; + uint thread_id = 0; + } + struct Thread { //Vector!VertexBlock block; RenderData[] render_list; - VertexBlock block; - Vector!VertexBlock blocks; + VertexBlock[] blocks; + Vector!VertexBlock filled_blocks; } Thread[] threads; @@ -224,6 +248,8 @@ struct Renderer struct DrawCall { + uint texture_id; + uint material_id; uint start; uint count; } @@ -248,6 +274,7 @@ struct Renderer void initialize() { + import ecs_utils.gfx.config; //this.technique = __ecs_used_technique; __initialize(this); @@ -260,7 +287,8 @@ struct Renderer threads = Mallocator.makeArray!Thread(32); foreach(ref Thread thread;threads) { - thread.block = getBlock(); + //thread.blocks = getBlock(); + thread.blocks = Mallocator.makeArray!VertexBlock(GfxConfig.materials.length); } } @@ -416,13 +444,15 @@ struct Renderer } - void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, short depth = 0, uint color = uint.max, float angle = 0, uint material_id = 0, uint mesh_id = 0, uint thread_id = 0) + //void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, short depth = 0, uint color = uint.max, float angle = 0, uint material_id = 0, uint mesh_id = 0, uint thread_id = 0) + void draw(scope ref const(DrawData) data) { if(prepared_items >= MaxObjects)return; - __draw(this,tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id); + __draw(this,data);//tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id); } - private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) + //private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) + private static void __draw_sdl(ref Renderer this_, scope ref const(DrawData) data) { /*with(this_) { @@ -442,22 +472,25 @@ struct Renderer }*/ } - private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) + // private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) + private static void __draw_gl(ref Renderer this_, scope ref const(DrawData) data) { //import core.stdc.string; with(this_) { //pos += view_pos; - size.x *= view_size.x; - size.y *= view_size.y; - pos.x = pos.x * view_size.x + view_pos.x; - pos.y = pos.y * view_size.y + view_pos.y;//*/ + vec2 pos = void; + vec2 size = void; + size.x = data.size.x * view_size.x; + size.y = data.size.y * view_size.y; + pos.x = data.position.x * view_size.x + view_pos.x; + pos.y = data.position.y * view_size.y + view_pos.y;//*/ /*version(ver6)void* ptr = ubos[0].mappedPointer() + data_index; else void* ptr = uniform_block.ptr + data_index;*/ if(data_ptr is null)return; void* ptr = data_ptr + data_index; - if(angle == 0) + if(data.angle == 0) { *cast(float*)ptr = size.x; *cast(float*)(ptr+4) = 0; @@ -467,8 +500,8 @@ struct Renderer else { //import core.stdc.math; - float sinn = sinf(angle); - float coss = cosf(angle); + float sinn = sinf(data.angle); + float coss = cosf(data.angle); *cast(float*)ptr = coss * size.x; *cast(float*)(ptr+4) = -sinn * size.y; *cast(float*)(ptr+8) = sinn * size.x; @@ -477,12 +510,12 @@ struct Renderer //memcpy(ptr,); memcpy(ptr+16,pos.data.ptr,8); - memcpy(ptr+32,coords.data.ptr,16); + memcpy(ptr+32,data.coords.data.ptr,16); //render_list[item_id] = RenderData(tex,material_id,mesh_id); - render_list[item_id].texture = tex; - render_list[item_id].material_id = material_id; - render_list[item_id].mesh_id = mesh_id; + render_list[item_id].texture = *cast(Texture*)&data.texture; + render_list[item_id].material_id = data.material_id; + render_list[item_id].mesh_id = data.mesh_id; data_index += data_offset; item_id++; @@ -490,18 +523,43 @@ struct Renderer } } - private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id = 0) + // private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id = 0) + private static void __draw_gl_vbo_batch(ref Renderer this_, scope ref const(DrawData) data) { import ecs_utils.gfx.config; - short[3] mem = [depth, *cast(short*)&color, *(cast(short*)&color + 1)]; + //import core.stdc.string; with(this_) { + uint thread_id = data.thread_id; //if(item_id >= MaxObjects)return; //pos += view_pos; + Thread* thread = &threads[thread_id]; + VertexBlock* block; + assert(thread.blocks.length > data.material_id); + block = &thread.blocks[data.material_id]; + if(block.items == 0) + { + thread.blocks[data.material_id] = getBlock(); + block = &thread.blocks[data.material_id]; + block.material_id = data.material_id; + } + else if(block.items >= VertexBlock.max_items) + { + //pushBlock(thread.block); + prepared_items += block.items; + thread.filled_blocks.add(*block); + thread.blocks[data.material_id] = getBlock(); + block = &thread.blocks[data.material_id]; + block.material_id = data.material_id; + } + + short[3] mem = [data.depth, *cast(short*)&data.color, *(cast(short*)&data.color + 1)]; - pos.x = pos.x * view_size.x + view_pos.x; - pos.y = pos.y * view_size.y + view_pos.y;//*/ + vec2 pos = void; + vec2 size = void; + pos.x = data.position.x * view_size.x + view_pos.x; + pos.y = data.position.y * view_size.y + view_pos.y;//*/ /*void* ptr = data_ptr + data_index; *cast(float*)ptr = size.x; @@ -512,13 +570,16 @@ struct Renderer memcpy(ptr+16,pos.data.ptr,8); memcpy(ptr+32,coords.data.ptr,16);*/ - short[] verts = cast(short[])threads[thread_id].block.batch_vertices; - uint item_id = threads[thread_id].block.items; + short[] verts = (cast(short*)block.batch_vertices.ptr)[0..block.batch_vertices.length>>1]; + uint item_id = block.items; - if(angle == 0) + uint mesh_id = data.mesh_id; + vec4 coords = data.coords; + + if(data.angle == 0) { - size.x *= view_size.x; - size.y *= view_size.y; + size.x = data.size.x * view_size.x; + size.y = data.size.y * view_size.y; verts[item_id*28] = cast(short)((GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x) * 8191); verts[item_id*28+1] = cast(short)((GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y) * 8191); @@ -550,10 +611,11 @@ struct Renderer else { //import core.stdc.math; - float sinx = sinf(angle) * size.x * view_size.y; - float cosx = cosf(angle) * size.x * view_size.x; - float siny = sinf(angle) * size.y * view_size.x; - float cosy = cosf(angle) * size.y * view_size.y; + float angle = data.angle; + float sinx = sinf(angle) * data.size.x * view_size.y; + float cosx = cosf(angle) * data.size.x * view_size.x; + float siny = sinf(angle) * data.size.y * view_size.x; + float cosy = cosf(angle) * data.size.y * view_size.y; /*batch_vertices[item_id*28] = GfxConfig.meshes[mesh_id].vertices[0] * size.x; batch_vertices[item_id*28+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y; @@ -618,7 +680,7 @@ struct Renderer uint ind_id = (item_id % batch_size)*4; - ushort[] indices = threads[thread_id].block.batch_indices; + ushort[] indices = block.batch_indices; indices[item_id*6] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[0] + ind_id); indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id); @@ -633,13 +695,7 @@ struct Renderer //render_list[item_id].mesh_id = mesh_id; //data_index += 1;//data_offset; - threads[thread_id].block.items++; - if(threads[thread_id].block.items >= VertexBlock.max_items) - { - //pushBlock(threads[thread_id].block); - threads[thread_id].blocks.add(threads[thread_id].block); - threads[thread_id].block = getBlock(); - } + block.items++; } } @@ -657,6 +713,7 @@ struct Renderer { glClearColor(0,0,0,0); glViewport(0,0,this_.resolution.x,this_.resolution.y); + glDepthMask(1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //glDisable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST); @@ -667,6 +724,10 @@ struct Renderer { glDepthRangef(0,1); } + else version(Android) + { + glDepthRangef(0,1); + } else { glDepthRange(0,1); @@ -822,9 +883,9 @@ struct Renderer //uint items = item_id/batch_size+1; foreach(i; 0..draw_list.length) { - if(material_id != render_list[i].material_id) + if(material_id != draw_list[i].material_id) { - material_id = render_list[i].material_id; + material_id = draw_list[i].material_id; GfxConfig.materials[material_id].bind(); float[3*4] data = [1,0,0,1,0,0,0,0,0,0,1,1]; GfxConfig.materials[material_id].pushUniforms(data.ptr); @@ -1054,7 +1115,8 @@ struct Renderer view_pos = (pos - size * 0.5) * view_size; } - __gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) __draw; + // __gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) __draw; + __gshared void function(ref Renderer this_, scope ref const(DrawData) data) __draw; __gshared void function(ref Renderer this_) __present; __gshared void function(ref Renderer this_) __clear; __gshared void function(ref Renderer this_) __initialize; diff --git a/demos/utils/source/ecs_utils/gfx/shader.d b/demos/utils/source/ecs_utils/gfx/shader.d index 5e24d9a..e941289 100644 --- a/demos/utils/source/ecs_utils/gfx/shader.d +++ b/demos/utils/source/ecs_utils/gfx/shader.d @@ -4,7 +4,9 @@ import bindbc.sdl; import bubel.ecs.std; -import glad.gl.gl; +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; //version = ver1; @@ -67,10 +69,12 @@ struct Shader } version(WebAssembly)const char* glsl = "#version 100\n"; + else version(Android)const char* glsl = "#version 100\n"; else const char* glsl = "#version 330\n"; const char* buffer = data.code.ptr; char* ver; version(WebAssembly)ver = cast(char*)"#define ver1 1\n#define GLES\n".ptr; + else version(Android)ver = cast(char*)"#define ver1 1\n#define GLES\n".ptr; else ver = cast(char*)"#define ver1 1\n".ptr; /*switch(__ecs_used_technique) { diff --git a/demos/utils/source/ecs_utils/gfx/texture.d b/demos/utils/source/ecs_utils/gfx/texture.d index d4e2089..73e9a54 100644 --- a/demos/utils/source/ecs_utils/gfx/texture.d +++ b/demos/utils/source/ecs_utils/gfx/texture.d @@ -6,7 +6,9 @@ import bubel.ecs.std; import ecs_utils.math.vector; -import glad.gl.gl; +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; extern(C): @@ -53,6 +55,7 @@ struct Texture data.bpp = surf.format.BytesPerPixel; data.data = Mallocator.makeArray!ubyte(surf.w*surf.h*surf.format.BytesPerPixel); data.data[0..$] = (cast(ubyte*)surf.pixels)[0..data.data.length]; + data.size = ivec2(surf.w, surf.h); SDL_FreeSurface(surf); @@ -65,8 +68,8 @@ struct Texture glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - if(data.bpp == 3)glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,surf.w,surf.h,0,GL_RGB,GL_UNSIGNED_BYTE,data.data.ptr); - else if(data.bpp == 4)glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,surf.w,surf.h,0,GL_RGBA,GL_UNSIGNED_BYTE,data.data.ptr); + if(data.bpp == 3)glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,data.size.x,data.size.y,0,GL_RGB,GL_UNSIGNED_BYTE,data.data.ptr); + else if(data.bpp == 4)glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,data.size.x,data.size.y,0,GL_RGBA,GL_UNSIGNED_BYTE,data.data.ptr); else return false; } @@ -80,7 +83,7 @@ struct Texture glBindTexture(GL_TEXTURE_2D, data.gl_handle); } - void destory() @nogc nothrow + void destroy() @nogc nothrow { if(data) { diff --git a/demos/utils/source/ecs_utils/imgui_bind.d b/demos/utils/source/ecs_utils/imgui_bind.d index b6572cb..a65b6b2 100644 --- a/demos/utils/source/ecs_utils/imgui_bind.d +++ b/demos/utils/source/ecs_utils/imgui_bind.d @@ -22,7 +22,9 @@ else : import bindbc.sdl; -import glad.gl.gl; +version(WebAssembly)import glad.gl.gles2; +else version(Android)import glad.gl.gles2; +else import glad.gl.gl; import cimgui.cimgui; @@ -210,7 +212,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor. // The function is only supported from SDL 2.0.4 (released Jan 2016) - bool any_mouse_button_down = ImGui::IsAnyMouseDown(); + bool any_mouse_button_down = ImGui.IsAnyMouseDown(); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); //#else*/ if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) @@ -291,10 +293,11 @@ void ImGuiImplSDL2NewFrame(SDL_Window* window) int w, h; int display_w, display_h; SDL_GetWindowSize(window, &w, &h); - SDL_GL_GetDrawableSize(window, &display_w, &display_h); + // SDL_GL_GetDrawableSize(window, &display_w, &display_h); FIXME: you see io.DisplaySize = ImVec2(cast(float)w, cast(float)h); - if (w > 0 && h > 0) - io.DisplayFramebufferScale = ImVec2(cast(float)display_w / w, cast(float)display_h / h); + // if (w > 0 && h > 0) + // io.DisplayFramebufferScale = ImVec2(cast(float)display_w / w, cast(float)display_h / h); + io.DisplayFramebufferScale = ImVec2(1,1); // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) frequency = SDL_GetPerformanceFrequency(); @@ -310,89 +313,460 @@ void ImGuiImplSDL2NewFrame(SDL_Window* window) } +__gshared GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) +__gshared char[32] g_GlslVersionString = ""; // Specified by user or detected based on compile time GL settings. +//__gshared GLuint g_FontTexture = 0; +__gshared GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; +__gshared int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location +__gshared int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location +__gshared uint g_VboHandle = 0, g_ElementsHandle = 0; - - - - - - - -bool ImGuiImplOpenGL2Init() +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { // Setup back-end capabilities flags ImGuiIO* io = igGetIO(); - io.BackendRendererName = "imgui_impl_opengl2"; + io.BackendRendererName = "imgui_impl_opengl3"; + + + // Store GLSL version string so we can refer to it later in case we recreate shaders. + // Note: GLSL version is NOT the same as GL version. Leave this to null if unsure. +/*#if defined(IMGUI_IMPL_OPENGL_ES2) + if (glsl_version == null) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) + if (glsl_version == null) + glsl_version = "#version 300 es"; +#elif defined(__APPLE__) + if (glsl_version == null) + glsl_version = "#version 150"; +#else + if (glsl_version == null) + glsl_version = "#version 130"; +#endif + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));*/ + //const (char)*glsl_version = "#version 100"; + import core.stdc.string; + strcpy(g_GlslVersionString.ptr, glsl_version); + strcat(g_GlslVersionString.ptr, "\n"); + + // Dummy construct to make it easily visible in the IDE and debugger which GL loader has been selected. + // The code actually never uses the 'gl_loader' variable! It is only here so you can read it! + // If auto-detection fails or doesn't select the same GL loader file as used by your application, + // you are likely to get a crash below. + // You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. + /*const char* gl_loader = "Unknown"; + IM_UNUSED(gl_loader); +#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) + gl_loader = "GL3W"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) + gl_loader = "GLEW"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) + gl_loader = "GLAD"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) + gl_loader = "glbinding2"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) + gl_loader = "glbinding3"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) + gl_loader = "custom"; +#else + gl_loader = "none"; +#endif*/ + + // Make a dummy GL call (we don't actually need the result) + // IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code. + // Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above. + /*GLint current_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);*/ + return true; } -void ImGuiImplOpenGL2Shutdown() +static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) { - ImGuiImplOpenGL2DestroyDeviceObjects(); -} - -void ImGuiImplOpenGL2NewFrame() -{ - if (!g_FontTexture) - ImGuiImplOpenGL2CreateDeviceObjects(); -} - -static void ImGuiImplOpenGL2SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) -{ - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - //glDisable(GL_LIGHTING); - //glDisable(GL_COLOR_MATERIAL); glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +// #ifdef GL_POLYGON_MODE +// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +// #endif - // If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!), - // you may need to backup/reset/restore current shader using the lines below. DO NOT MODIFY THIS FILE! Add the code in your calling function: - // GLint last_program; - // glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - // glUseProgram(0); - // ImGui_ImplOpenGL2_RenderDrawData(...); - // glUseProgram(last_program) + // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) + bool clip_origin_lower_left = true; +// #if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__) +// GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin); +// if (current_clip_origin == GL_UPPER_LEFT) +// clip_origin_lower_left = false; +// #endif // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data.DisplayPos (top left) to draw_data.DisplayPos+data_data.DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. glViewport(0, 0, cast(GLsizei)fb_width, cast(GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(draw_data.DisplayPos.x, draw_data.DisplayPos.x + draw_data.DisplaySize.x, draw_data.DisplayPos.y + draw_data.DisplaySize.y, draw_data.DisplayPos.y, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + float L = draw_data.DisplayPos.x; + float R = draw_data.DisplayPos.x + draw_data.DisplaySize.x; + float T = draw_data.DisplayPos.y; + float B = draw_data.DisplayPos.y + draw_data.DisplaySize.y; + if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left + const float[4][4] ortho_projection = + [ + [ 2.0f/(R-L), 0.0f, 0.0f, 0.0f ], + [ 0.0f, 2.0f/(T-B), 0.0f, 0.0f ], + [ 0.0f, 0.0f, -1.0f, 0.0f ], + [ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f ], + ]; + glUseProgram(g_ShaderHandle); + glUniform1i(g_AttribLocationTex, 0); + glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); +// #ifdef GL_SAMPLER_BINDING +// glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. +// #endif + +// (void)vertex_array_object; +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// glBindVertexArray(vertex_array_object); +// #endif + + // Bind vertex/index buffers and setup attributes for ImDrawVert + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + glEnableVertexAttribArray(g_AttribLocationVtxPos); + glEnableVertexAttribArray(g_AttribLocationVtxUV); + glEnableVertexAttribArray(g_AttribLocationVtxColor); + glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.pos.offsetof); + glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.uv.offsetof); + glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.col.offsetof); } -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGuiImplOpenGL2RenderDrawData(ImDrawData* draw_data) + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL3_NewFrame() +{ + if (!g_ShaderHandle) + ImGui_ImplOpenGL3_CreateDeviceObjects(); +} + +bool ImGui_ImplOpenGL3_CreateDeviceObjects() +{ + // Backup GL state + GLint last_texture, last_array_buffer; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// GLint last_vertex_array; +// glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +// #endif + + // Parse GLSL version string + import core.stdc.stdio; + int glsl_version = 130; + sscanf(g_GlslVersionString.ptr, "#version %d", &glsl_version); + + const GLchar* vertex_shader_glsl_120 = + "uniform mat4 ProjMtx;\n + attribute vec2 Position;\n + attribute vec2 UV;\n + attribute vec4 Color;\n + varying vec2 Frag_UV;\n + varying vec4 Frag_Color;\n + void main()\n + {\n + Frag_UV = UV;\n + Frag_Color = Color;\n + gl_Position = ProjMtx * vec4(Position.xy,0,1);\n + }\n"; + + const GLchar* vertex_shader_glsl_130 = + "uniform mat4 ProjMtx;\n + in vec2 Position;\n + in vec2 UV;\n + in vec4 Color;\n + out vec2 Frag_UV;\n + out vec4 Frag_Color;\n + void main()\n + {\n + Frag_UV = UV;\n + Frag_Color = Color;\n + gl_Position = ProjMtx * vec4(Position.xy,0,1);\n + }\n"; + + const GLchar* vertex_shader_glsl_300_es = + "precision mediump float;\n + layout (location = 0) in vec2 Position;\n + layout (location = 1) in vec2 UV;\n + layout (location = 2) in vec4 Color;\n + uniform mat4 ProjMtx;\n + out vec2 Frag_UV;\n + out vec4 Frag_Color;\n + void main()\n + {\n + Frag_UV = UV;\n + Frag_Color = Color;\n + gl_Position = ProjMtx * vec4(Position.xy,0,1);\n + }\n"; + + const GLchar* vertex_shader_glsl_410_core = + "layout (location = 0) in vec2 Position;\n + layout (location = 1) in vec2 UV;\n + layout (location = 2) in vec4 Color;\n + uniform mat4 ProjMtx;\n + out vec2 Frag_UV;\n + out vec4 Frag_Color;\n + void main()\n + {\n + Frag_UV = UV;\n + Frag_Color = Color;\n + gl_Position = ProjMtx * vec4(Position.xy,0,1);\n + }\n"; + + const GLchar* fragment_shader_glsl_120 = + "#ifdef GL_ES\n + precision mediump float;\n + #endif\n + uniform sampler2D Texture;\n + varying vec2 Frag_UV;\n + varying vec4 Frag_Color;\n + void main()\n + {\n + gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n + }\n"; + + const GLchar* fragment_shader_glsl_130 = + "uniform sampler2D Texture;\n + in vec2 Frag_UV;\n + in vec4 Frag_Color;\n + out vec4 Out_Color;\n + void main()\n + {\n + Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n + }\n"; + + const GLchar* fragment_shader_glsl_300_es = + "precision mediump float;\n + uniform sampler2D Texture;\n + in vec2 Frag_UV;\n + in vec4 Frag_Color;\n + layout (location = 0) out vec4 Out_Color;\n + void main()\n + {\n + Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n + }\n"; + + const GLchar* fragment_shader_glsl_410_core = + "in vec2 Frag_UV;\n + in vec4 Frag_Color;\n + uniform sampler2D Texture;\n + layout (location = 0) out vec4 Out_Color;\n + void main()\n + {\n + Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n + }\n"; + + // Select shaders matching our GLSL versions + const (char)* vertex_shader = null; + const (char)* fragment_shader = null; + if (glsl_version < 130) + { + vertex_shader = vertex_shader_glsl_120; + fragment_shader = fragment_shader_glsl_120; + } + else if (glsl_version >= 410) + { + vertex_shader = vertex_shader_glsl_410_core; + fragment_shader = fragment_shader_glsl_410_core; + } + else if (glsl_version == 300) + { + vertex_shader = vertex_shader_glsl_300_es; + fragment_shader = fragment_shader_glsl_300_es; + } + else + { + vertex_shader = vertex_shader_glsl_130; + fragment_shader = fragment_shader_glsl_130; + } + + // Create shaders + const (char)*[2] vertex_shader_with_version = [ g_GlslVersionString.ptr, vertex_shader ]; + g_VertHandle = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(g_VertHandle, 2, vertex_shader_with_version.ptr, null); + glCompileShader(g_VertHandle); + CheckShader(g_VertHandle, "vertex shader"); + + const (char)*[2] fragment_shader_with_version = [ g_GlslVersionString.ptr, fragment_shader ]; + g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_FragHandle, 2, fragment_shader_with_version.ptr, null); + glCompileShader(g_FragHandle); + CheckShader(g_FragHandle, "fragment shader"); + + g_ShaderHandle = glCreateProgram(); + glAttachShader(g_ShaderHandle, g_VertHandle); + glAttachShader(g_ShaderHandle, g_FragHandle); + glLinkProgram(g_ShaderHandle); + CheckProgram(g_ShaderHandle, "shader program"); + + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); + g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); + g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position"); + g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV"); + g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color"); + + // Create buffers + glGenBuffers(1, &g_VboHandle); + glGenBuffers(1, &g_ElementsHandle); + + ImGui_ImplOpenGL3_CreateFontsTexture(); + + // Restore modified GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// glBindVertexArray(last_vertex_array); +// #endif + + return true; +} + +void ImGui_ImplOpenGL3_DestroyDeviceObjects() +{ + if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; } + if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; } + if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); } + if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); } + if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; } + if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; } + if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; } + + ImGui_ImplOpenGL3_DestroyFontsTexture(); +} + +static bool CheckShader(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetShaderiv(handle, GL_COMPILE_STATUS, &status); + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); + /*if (cast(GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); + if (log_length > 1) + { + ImVector buf; + buf.resize(cast(int)(log_length + 1)); + glGetShaderInfoLog(handle, log_length, null, cast(GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + }*/ + return cast(GLboolean)status == GL_TRUE; +} + +// If you get an error please report on GitHub. You may try different GL context version or GLSL version. +static bool CheckProgram(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetProgramiv(handle, GL_LINK_STATUS, &status); + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); + /*if (cast(GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); + if (log_length > 1) + { + ImVector buf; + buf.resize(cast(int)(log_length + 1)); + glGetProgramInfoLog(handle, log_length, null, cast(GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + }*/ + return cast(GLboolean)status == GL_TRUE; +} + +bool ImGui_ImplOpenGL3_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO* io = igGetIO(); + ubyte* pixels; + int width, height, bpp; + + ImFontAtlas_GetTexDataAsRGBA32(io.Fonts,&pixels, &width, &height, &bpp); + //io.Fonts.GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +// #ifdef GL_UNPACK_ROW_LENGTH +// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +// #endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts.TexID = cast(ImTextureID)cast(sizediff_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplOpenGL3_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO* io = igGetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts.TexID = null; + g_FontTexture = 0; + } +} + +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) int fb_width = cast(int)(draw_data.DisplaySize.x * draw_data.FramebufferScale.x); int fb_height = cast(int)(draw_data.DisplaySize.y * draw_data.FramebufferScale.y); - if (fb_width == 0 || fb_height == 0) + if (fb_width <= 0 || fb_height <= 0) return; // Backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint[2] last_polygon_mode; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode.ptr); +// #ifdef GL_SAMPLER_BINDING +// GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); +// #endif + GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object); +// #endif +// #ifdef GL_POLYGON_MODE +// GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); +// #endif GLint[4] last_viewport; glGetIntegerv(GL_VIEWPORT, last_viewport.ptr); GLint[4] last_scissor_box; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); // Setup desired GL state - ImGuiImplOpenGL2SetupRenderState(draw_data, fb_width, fb_height); + // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + GLuint vertex_array_object = 0; +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// glGenVertexArrays(1, &vertex_array_object); +// #endif + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data.DisplayPos; // (0,0) unless using multi-viewports @@ -401,23 +775,22 @@ void ImGuiImplOpenGL2RenderDrawData(ImDrawData* draw_data) // Render command lists for (int n = 0; n < draw_data.CmdListsCount; n++) { - ImDrawList* cmd_list = draw_data.CmdLists[n]; - ImDrawVert* vtx_buffer = cmd_list.VtxBuffer.Data; - ImDrawIdx* idx_buffer = cmd_list.IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, ImDrawVert.sizeof, cast(const GLvoid*)(cast(const char*)vtx_buffer + ImDrawVert.pos.offsetof)); - glTexCoordPointer(2, GL_FLOAT, ImDrawVert.sizeof, cast(const GLvoid*)(cast(const char*)vtx_buffer + ImDrawVert.uv.offsetof)); - glColorPointer(4, GL_UNSIGNED_BYTE, ImDrawVert.sizeof, cast(const GLvoid*)(cast(const char*)vtx_buffer + ImDrawVert.col.offsetof)); + const ImDrawList* cmd_list = draw_data.CmdLists[n]; + + // Upload vertex/index buffers + glBufferData(GL_ARRAY_BUFFER, cast(GLsizeiptr)cmd_list.VtxBuffer.Size * ImDrawVert.sizeof, cast(const GLvoid*)cmd_list.VtxBuffer.Data, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, cast(GLsizeiptr)cmd_list.IdxBuffer.Size * ImDrawIdx.sizeof, cast(const GLvoid*)cmd_list.IdxBuffer.Data, GL_STREAM_DRAW); for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list.CmdBuffer.Data[cmd_i]; - if (pcmd.UserCallback) + if (pcmd.UserCallback != null) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - /*if (pcmd.UserCallback == &ImDrawCallback_ResetRenderState) - ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height); - else*/ + // if (pcmd.UserCallback == ImDrawCallback_ResetRenderState) + // ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + // else pcmd.UserCallback(cmd_list, pcmd); } else @@ -435,74 +808,43 @@ void ImGuiImplOpenGL2RenderDrawData(ImDrawData* draw_data) glScissor(cast(int)clip_rect.x, cast(int)(fb_height - clip_rect.w), cast(int)(clip_rect.z - clip_rect.x), cast(int)(clip_rect.w - clip_rect.y)); // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, cast(GLuint)pcmd.TextureId); - glDrawElements(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, ImDrawIdx.sizeof == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + glBindTexture(GL_TEXTURE_2D, cast(GLuint)cast(sizediff_t)pcmd.TextureId); +// #if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET +// if (g_GlVersion >= 320) +// glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd.ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd.IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd.VtxOffset); +// else +// #endif + glDrawElements(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, ImDrawIdx.sizeof == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, cast(void*)cast(sizediff_t)(pcmd.IdxOffset * ImDrawIdx.sizeof)); } } - idx_buffer += pcmd.ElemCount; } } + // Destroy the temporary VAO +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// glDeleteVertexArrays(1, &vertex_array_object); +// #endif + // Restore modified GL state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, cast(GLuint)last_texture); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, cast(GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, cast(GLenum)last_polygon_mode[1]); + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); +// #ifdef GL_SAMPLER_BINDING +// glBindSampler(0, last_sampler); +// #endif + glActiveTexture(last_active_texture); +// #ifndef IMGUI_IMPL_OPENGL_ES2 +// glBindVertexArray(last_vertex_array_object); +// #endif + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); +// #ifdef GL_POLYGON_MODE +// glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); +// #endif glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]); glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]); -} - -bool ImGuiImplOpenGL2CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO* io = igGetIO(); - ubyte* pixels; - int width, height; - int bpp; - ImFontAtlas_GetTexDataAsRGBA32(io.Fonts, &pixels, &width, &height, &bpp); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts.TexID = cast(ImTextureID)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGuiImplOpenGL2DestroyFontsTexture() -{ - if (g_FontTexture) - { - ImGuiIO* io = igGetIO(); - glDeleteTextures(1, &g_FontTexture); - io.Fonts.TexID = null; - g_FontTexture = 0; - } -} - -bool ImGuiImplOpenGL2CreateDeviceObjects() -{ - return ImGuiImplOpenGL2CreateFontsTexture(); -} - -void ImGuiImplOpenGL2DestroyDeviceObjects() -{ - ImGuiImplOpenGL2DestroyFontsTexture(); } \ No newline at end of file diff --git a/demos/utils/source/ecs_utils/math/vector.d b/demos/utils/source/ecs_utils/math/vector.d index a8f14f8..dcec8df 100644 --- a/demos/utils/source/ecs_utils/math/vector.d +++ b/demos/utils/source/ecs_utils/math/vector.d @@ -1,5 +1,7 @@ module ecs_utils.math.vector; +import ecs_utils.utils; + struct vec2 { this(float v) @nogc nothrow @@ -42,6 +44,11 @@ struct vec2 else static assert(0, "Operator "~op~" not implemented"); } + vec2 opUnary(string op)()if (op == "-") + { + return vec2(-x,-y); + } + ivec2 opCast() { return ivec2(cast(int)x,cast(int)y); @@ -71,6 +78,31 @@ struct vec2 } else static assert(0, "Operator "~op~" not implemented"); } + + float length2() + { + return x*x + y*y; + } + + float length() + { + return sqrtf(length2); + } + + float fastSqrLength() + { + return rsqrt(length2); + } + + vec2 normalize() + { + return this * fastSqrLength(); + } +} + +float dot(vec2 a, vec2 b) +{ + return a.x*b.x + a.y*b.y; } struct vec4 diff --git a/demos/utils/source/ecs_utils/utils.d b/demos/utils/source/ecs_utils/utils.d index 8ce5cd1..1896856 100644 --- a/demos/utils/source/ecs_utils/utils.d +++ b/demos/utils/source/ecs_utils/utils.d @@ -2,13 +2,26 @@ module ecs_utils.utils; extern(C): -int randomRange(int min, int max) +import ecs_utils.math.vector; + +enum PI = 3.141592653589793238462643383279502884197169399375105820; + +extern(C) float sqrtf(float x) @nogc nothrow @system; +extern(C) float acosf(float x) @nogc nothrow @system; +extern(C) float sinf(float x) @nogc nothrow @system; +extern(C) float cosf(float x) @nogc nothrow @system; +extern(C) float powf(float x, float y) @nogc nothrow @system; +extern(C) float fabs(float x) @nogc nothrow @system; +extern(C) float log2f(float arg) @nogc nothrow @system; + + +int randomRange(int min, int max) nothrow @nogc @trusted { int range = max - min; return rand() % range - min; } -float randomf() +float randomf() nothrow @nogc @trusted { const float scale = 1.0 / 32_767.0; return cast(float)(rand() & 0x007FFF) * scale; @@ -21,6 +34,38 @@ float randomRangef(float min, float max) return rand()%4096; }*/ +float mix(float x, float y, float a) +{ + //return x*a + y*(a-1); + //return x*a + y*a - y; + return x*(a+y) - y; +} + +float rsqrt(float number) +{ + long i; + float x2, y; + const float threehalfs = 1.5F; + + x2 = number * 0.5F; + y = number; + i = * cast( long * ) &y; // evil floating point bit level hacking + i = 0x5f3759df - ( i >> 1 ); // what the fuck? + y = * cast( float * ) &i; + y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration + + return y; +} + +vec2 randomCircularSample() nothrow @nogc @trusted +{ + float angle = 2 * PI * randomf; + float radius = sqrtf(randomf); + float s = sinf(angle); + float c = cosf(angle); + return vec2(c,s)*radius; +} + version(GNU) { public import core.stdc.stdio : printf; @@ -32,9 +77,13 @@ version(GNU) else { extern(C) int printf(scope const char* format, ...) @nogc nothrow @system; - public import std.array : staticArray; + // public import std.array : staticArray; + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } } -extern(C) int rand(); +extern(C) int rand() nothrow @nogc @trusted; version(D_BetterC) { @@ -58,7 +107,15 @@ version(D_BetterC) } } +version(Android) +{ + alias pthread_key_t = uint; + extern (C) int pthread_key_create(pthread_key_t *, void* function(void *)) @nogc nothrow; + extern (C) int pthread_key_delete(pthread_key_t) @nogc nothrow; + extern (C) void* pthread_getspecific(pthread_key_t) @nogc nothrow; + extern (C) int pthread_setspecific(pthread_key_t, const void *) @nogc nothrow; +} version(WebAssembly) { diff --git a/dub.json b/dub.json index 01cf85c..1a99040 100755 --- a/dub.json +++ b/dub.json @@ -1,5 +1,6 @@ { - "name": "ecs", + "name": "bubel_ecs", + "targetName" : "ecs", "authors": [ "MichaƂ Masiukiewicz", "Dawid Masiukiewicz" ], @@ -118,7 +119,8 @@ "-unittest" ], "dflags-gdc": [ - "-fno-druntime" + "-fno-druntime", + "-lpthread" ], "sourcePaths": ["source/","tests/"], "mainSourceFile":"tests/runner.d", diff --git a/meson.build b/meson.build index c31b93a..4951584 100644 --- a/meson.build +++ b/meson.build @@ -23,6 +23,8 @@ tests_src = [ ] betterC_opt = get_option('betterC') +BuildDemos_opt = get_option('BuildDemos') +LTO_otp = get_option('LTO') comp = meson.get_compiler('d') @@ -31,16 +33,43 @@ comp_id = comp.get_id() args = [] link_args = [] +if comp_id == 'gcc' + args += '-pthread' + link_args += '-pthread' +endif + +if LTO_otp + if comp_id == 'gcc' + args += '-flto' + link_args += '-flto' + elif comp_id == 'llvm' + args += '-flto=thin' + link_args += '-flto=thin' + else + message('LTO don\'t work with DMD') + endif +endif + if betterC_opt - args += '-betterC' - link_args += '-betterC' + if comp_id == 'gcc' + args += ['-fno-druntime'] + link_args += ['-fno-druntime'] + else + args += '-betterC' + link_args += '-betterC' + endif endif inc = include_directories('source/') tests_inc = include_directories('source/') -ecs_lib = shared_library('ecs', src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args) +ecs_lib = library('ecs', src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args) executable('tests', tests_src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args, link_with: ecs_lib) +bubel_ecs_dep = declare_dependency(include_directories : [inc], link_with : ecs_lib) +if BuildDemos_opt + subdir('demos/utils') + subdir('demos') +endif diff --git a/meson_options.txt b/meson_options.txt index fdf13a4..0ea2df9 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,3 @@ -option('betterC', type: 'boolean', value: false) \ No newline at end of file +option('betterC', type: 'boolean', value: false) +option('BuildDemos', type: 'boolean', value: false) +option('LTO', type: 'boolean', value: false) \ No newline at end of file diff --git a/source/bubel/ecs/block_allocator.d b/source/bubel/ecs/block_allocator.d index d3070c4..6894a17 100644 --- a/source/bubel/ecs/block_allocator.d +++ b/source/bubel/ecs/block_allocator.d @@ -54,6 +54,7 @@ struct BlockAllocator Mallocator.dispose(pointers); pointers = next_pointers; } + next_block = null; } private: diff --git a/source/bubel/ecs/entity.d b/source/bubel/ecs/entity.d index 6a64d50..72131b5 100644 --- a/source/bubel/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -34,15 +34,26 @@ struct Entity */ T* getComponent(T)() const { - EntityManager.EntitiesBlock* block = gEM.getMetaData(&this); + /*EntityManager.EntitiesBlock* block = gEM.getMetaData(&this); EntityManager.EntityInfo* info = block.type_info; if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) return null; - return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof); + return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof);*/ + return cast(T*)getComponent(T.component_id); } - bool hasComponent(ushort component_id) + void* getComponent(ushort component_id) const + { + EntityManager.EntitiesBlock* block = gEM.getMetaData(&this); + EntityManager.EntityInfo* info = block.type_info; + if (component_id >= info.deltas.length || info.deltas[component_id] == 0) + return null; + + return (cast(void*)block + info.deltas[component_id] + block.entityIndex(&this) * gEM.components[component_id].size); + } + + bool hasComponent(ushort component_id) const { EntityManager.EntitiesBlock* block = gEM.getMetaData(&this); EntityManager.EntityInfo* info = block.type_info; @@ -50,7 +61,7 @@ struct Entity return true; } - EntityMeta getMeta() + EntityMeta getMeta() const { EntityMeta meta; meta.block = gEM.getMetaData(&this); @@ -66,15 +77,26 @@ struct EntityMeta T* getComponent(T)() const { - const (EntityManager.EntityInfo)* info = block.type_info; + /*const (EntityManager.EntityInfo)* info = block.type_info; if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) return null; - return cast(T*)(cast(void*)block + block.type_info.deltas[T.component_id] + index * T.sizeof); + return cast(T*)(cast(void*)block + info.deltas[T.component_id] + index * T.sizeof);*/ + return cast(T*)getComponent(T.component_id); + } + + void* getComponent(ushort component_id) const + { + const (EntityManager.EntityInfo)* info = block.type_info; + + if (component_id >= info.deltas.length || info.deltas[component_id] == 0) + return null; + + return (cast(void*)block + info.deltas[component_id] + index * gEM.components[component_id].size); } - bool hasComponent(ushort component_id) + bool hasComponent(ushort component_id) const { - EntityManager.EntityInfo* info = block.type_info; + const EntityManager.EntityInfo* info = block.type_info; if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false; return true; } @@ -106,6 +128,15 @@ export struct EntityTemplate if(T.component_id >= info.tmpl_deltas.length || info.tmpl_deltas[T.component_id] == ushort.max)return null; return cast(T*)(entity_data.ptr + info.tmpl_deltas[T.component_id]); } + + /************************************************************************************************************************ + Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. + */ + void* getComponent(ushort component_id) const nothrow @nogc + { + if(component_id >= info.tmpl_deltas.length || info.tmpl_deltas[component_id] == ushort.max)return null; + return cast(void*)(entity_data.ptr + info.tmpl_deltas[component_id]); + } } /************************************************************************************************************************ diff --git a/source/bubel/ecs/id_manager.d b/source/bubel/ecs/id_manager.d index aaeef30..86a611e 100644 --- a/source/bubel/ecs/id_manager.d +++ b/source/bubel/ecs/id_manager.d @@ -15,7 +15,7 @@ struct IDManager /************************************************************************************************************************ Get new ID. */ - pragma(inline, false) EntityID getNewID() nothrow @nogc + EntityID getNewID() nothrow @nogc { int current = m_stack_top.atomicOp!"-="(1) + 1; if (current < 0) @@ -177,15 +177,9 @@ struct IDManager if (m_last_id > m_ids_array.length) { uint begin = cast(uint) m_ids_array.length; - Data[] new_array = Mallocator.makeArray!Data(begin + (m_blocks_count << 16)); - memcpy(new_array.ptr, m_ids_array.ptr, m_ids_array.length * Data.sizeof); - Mallocator.dispose(m_ids_array); - m_ids_array = new_array; - uint[] new_stack = Mallocator.makeArray!uint(m_ids_array.length); - memcpy(new_stack.ptr, m_free_stack.ptr, m_free_stack.length * uint.sizeof); - Mallocator.dispose(m_free_stack); - m_free_stack = new_stack; + m_ids_array = Mallocator.resizeArray(m_ids_array, begin + (m_blocks_count << 16)); + m_free_stack = Mallocator.resizeArray(m_free_stack, m_ids_array.length); foreach (block; m_blocks[0 .. m_blocks_count - 1]) { diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 0982531..e82add3 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -96,29 +96,7 @@ export struct EntityManager { foreach (ref system; systems) { - system.disable(); - if (system.m_destroy) - (cast(void function(void*)) system.m_destroy)(system.m_system_pointer); - - if (system.jobs) - Mallocator.dispose(system.jobs); - if (system.m_read_only_components) - Mallocator.dispose(system.m_read_only_components); - if (system.m_writable_components) - Mallocator.dispose(system.m_writable_components); - if (system.m_components) - Mallocator.dispose(system.m_components); - if (system.m_excluded_components) - Mallocator.dispose(system.m_excluded_components); - if (system.m_optional_components) - Mallocator.dispose(system.m_optional_components); - if (system.m_name) - Mallocator.dispose(system.m_name); - if (system.m_event_callers) - Mallocator.dispose(system.m_event_callers); - - if (system.m_system_pointer) - Mallocator.dispose(system.m_system_pointer); + system.destroy(); } foreach (EntityInfo* info; &entities_infos.byValue) @@ -382,6 +360,10 @@ export struct EntityManager else assert(pass < passes.length, "Update pass (ID " ~ pass.to!string ~ ") doesn't exist."); + // enum SystemName = fullyQualifiedName!Sys; + enum SystemName = fullName!Sys; + //enum SystemName = Sys.stringof; + System system; system.m_pass = pass; @@ -418,9 +400,11 @@ export struct EntityManager static if (Params.length == 2 && is(Params[0] == Entity*)) { alias EventParamType = Params[1]; - enum EventName = Unqual!(EventParamType).stringof; + enum EventName = fullName!(Unqual!(EventParamType)); + // enum EventName = fullyQualifiedName!(Unqual!(EventParamType));//.stringof; ushort evt = events_map.get(cast(char[]) EventName, ushort.max); - assert(evt != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(evt != ushort.max, + "Can't register system \"" ~ SystemName ~ "\" due to non existing event \"" ~ EventName ~ "\"."); callers[i].callback = cast(void*)&callEventHandler!(EventParamType); @@ -456,7 +440,7 @@ export struct EntityManager uint writable_dep = 1; } - static ComponentsCounts getComponentsCounts()() + static ComponentsCounts getComponentsCounts() { ComponentsCounts components_counts; @@ -479,7 +463,8 @@ export struct EntityManager string name; static if (isArray!MemberType) { // Workaround. This code is never called with: not an array type, but compiler prints an error - name = Unqual!(ForeachType!MemberType).stringof; + // name = fullyQualifiedName!(Unqual!(ForeachType!MemberType));//.stringof; + name = fullName!(Unqual!(ForeachType!MemberType)); } bool is_optional; @@ -679,7 +664,7 @@ export struct EntityManager } - static ComponentsIndices!component_counts getComponentsInfo()() + static ComponentsIndices!component_counts getComponentsInfo() { ComponentsIndices!component_counts components_info; @@ -704,7 +689,9 @@ export struct EntityManager string name; static if (isArray!MemberType) { // Workaround. This code is never called with: not an array type, but compiler prints an error - name = Unqual!(ForeachType!MemberType).stringof; + // name = fullyQualifiedName!(Unqual!(ForeachType!MemberType)); + name = fullName!(Unqual!(ForeachType!MemberType)); + //name = Unqual!(ForeachType!MemberType).stringof; } bool is_optional; @@ -759,7 +746,8 @@ export struct EntityManager { foreach (str; Sys.ExcludedComponents) { - components_info.addExcluded(CompInfo(str.stringof, str.stringof)); + components_info.addExcluded(CompInfo(str.stringof, fullName!str)); + // components_info.addExcluded(CompInfo(str.stringof, str.stringof)); } } @@ -786,7 +774,7 @@ export struct EntityManager enum ComponentsIndices!component_counts components_info = getComponentsInfo(); - static void genCompList()(ref System system, ref HashMap!(char[], ushort) components_map) + static void genCompList(ref System system, ref HashMap!(char[], ushort) components_map) { foreach (member; __traits(allMembers, Sys.EntitiesData)) @@ -828,10 +816,10 @@ export struct EntityManager ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); system.m_components[iii] = comp; } @@ -840,10 +828,10 @@ export struct EntityManager ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); system.m_excluded_components[iii] = comp; } @@ -852,10 +840,10 @@ export struct EntityManager ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); system.m_optional_components[iii] = comp; } @@ -864,10 +852,10 @@ export struct EntityManager ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); system.m_read_only_components[iii] = comp; } @@ -876,16 +864,16 @@ export struct EntityManager ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); system.m_writable_components[iii] = comp; } } - static void fillInputData()(ref Sys.EntitiesData input_data, EntityInfo* info, + static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info, EntitiesBlock* block, uint offset, uint entities_count, System* system) { //enum ComponentsIndices components_info = getComponentsInfo(); @@ -906,7 +894,9 @@ export struct EntityManager input_data.thread_id = cast(typeof(input_data.thread_id))threadID(); }//*/ - static foreach (iii, comp_info; components_info.req) + ///FIXME: should be "components_info.req()" but it's not compile with GCC + static foreach (iii, comp_info; components_info.m_req[0 + .. components_info.m_req_counter]) { __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name)))*)( @@ -914,7 +904,8 @@ export struct EntityManager .. entities_count]; } - static foreach (iii, comp_info; components_info.optional) + static foreach (iii, comp_info; components_info.m_optional[0 + .. components_info.m_optional_counter]) { if (system.m_optional_components[iii] < info.deltas.length && info.deltas[system.m_optional_components[iii]] != 0) @@ -928,7 +919,7 @@ export struct EntityManager } } - /*bool checkOnUpdateParams()() + /*bool checkOnUpdateParams() { bool ret = false; foreach (func; __traits(getOverloads, Sys, "onUpdate")) @@ -1005,26 +996,30 @@ export struct EntityManager else entities_count = block.entities_count; - assert(entities_count <= block.entities_count - && offset <= block.entities_count); - - fillInputData(input_data, info, block, offset, entities_count, system); - - static if (hasMember!(Sys.EntitiesData, "thread_id")) + if (entities_count > 0) { - input_data.thread_id = cast(typeof(input_data.thread_id)) data - .thread_id; + assert(entities_count <= block.entities_count + && offset < block.entities_count); + assert(entities_count > offset); + + fillInputData(input_data, info, block, offset, entities_count, system); + + static if (hasMember!(Sys.EntitiesData, "thread_id")) + { + input_data.thread_id = cast( + typeof(input_data.thread_id)) data.thread_id; + } + + static if (hasMember!(Sys.EntitiesData, "job_id")) + { + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; + } + + //s.onUpdate(input_data); + (cast(typeof(&__traits(getOverloads, s, + "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)( + input_data); } - - static if (hasMember!(Sys.EntitiesData, "job_id")) - { - input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; - } - - //s.onUpdate(input_data); - (cast(typeof(&__traits(getOverloads, s, - "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)(input_data); - block = block.next_block; offset = 0; blocks--; @@ -1115,6 +1110,32 @@ export struct EntityManager } } + static void catchEntityFilterFunction(string func_name, RetType = void)(void** member) + { + static if (hasMember!(Sys, func_name)) + { + foreach (func; __traits(getOverloads, Sys, func_name)) + { + static if ((Parameters!(func)).length == 1 + && is(Parameters!(func)[0] == EntityInfo*) + && is(ReturnType!(func) == RetType)) + { + static RetType callFunc(void* system_pointer, EntityInfo* info) + { + Sys* s = cast(Sys*) system_pointer; + static if (is(RetTyp == void)) + mixin("s." ~ func_name ~ "(info)"); + else + return mixin("s." ~ func_name ~ "(info)"); + } + + *member = cast(void*)&callFunc; + break; + } + } + } + } + catchFunction!("onEnable")(&system.m_enable); catchFunction!("onDisable")(&system.m_disable); catchFunction!("onCreate")(&system.m_create); @@ -1126,6 +1147,8 @@ export struct EntityManager catchEntityFunction!("onRemoveEntity")(&system.m_remove_entity); catchEntityFunction!("onChangeEntity")(&system.m_change_entity); + catchEntityFilterFunction!("filterEntity", bool)(&system.m_filter_entity); + system.m_system_pointer = cast(void*) Mallocator.make!Sys; system.m_priority = priority; //(cast(Sys*) system.m_system_pointer).__ecsInitialize(); @@ -1148,10 +1171,10 @@ export struct EntityManager ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing dependency."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); system.m_readonly_dependencies[iii] = comp; } @@ -1161,21 +1184,20 @@ export struct EntityManager ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); version (D_BetterC) assert(comp != ushort.max, - "Can't register system \"" ~ Sys.stringof + "Can't register system \"" ~ SystemName ~ "\" due to non existing dependency."); else - assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + assert(comp != ushort.max, "Can't register system \"" ~ SystemName ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); system.m_writable_dependencies[iii] = comp; } - ushort sys_id = systems_map.get(cast(char[]) Sys.stringof, ushort.max); + ushort sys_id = systems_map.get(cast(char[]) SystemName, ushort.max); if (sys_id < systems.length) { - systems[sys_id].disable(); - if (systems[sys_id].m_destroy) - (cast(void function(void*)) systems[sys_id].m_destroy)( - systems[sys_id].m_system_pointer); + system.m_name = systems[sys_id].m_name; + systems[sys_id].m_name = null; + systems[sys_id].destroy(); if (system.m_create) (cast(void function(void*)) system.m_create)(system.m_system_pointer); @@ -1183,12 +1205,11 @@ export struct EntityManager system.enable(); system.m_id = sys_id; - system.m_name = systems[sys_id].m_name; systems[sys_id] = system; } else { - system.m_name = Mallocator.makeArray(cast(char[]) Sys.stringof); + system.m_name = Mallocator.makeArray(cast(char[]) SystemName); systems_map.add(system.m_name, cast(ushort) systems.length); @@ -1247,6 +1268,10 @@ export struct EntityManager { ComponentInfo info; + // enum ComponentName = fullyQualifiedName!Comp; + enum ComponentName = fullName!Comp; + // enum ComponentName = Comp.stringof; + static if (!(hasMember!(Comp, "component_id")) || !is(typeof(Comp.component_id) == ushort)) { static assert(0, "Add \"mixin ECS.Component;\" in top of component structure;"); @@ -1283,19 +1308,19 @@ export struct EntityManager info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof); *cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); - ushort comp_id = components_map.get(cast(char[]) Comp.stringof, ushort.max); + ushort comp_id = components_map.get(cast(char[]) ComponentName, ushort.max); if (comp_id < components.length) { Comp.component_id = comp_id; + if (components[comp_id].init_data) + Mallocator.dispose(components[comp_id].init_data); components[comp_id] = info; } else { components.add(info); Comp.component_id = cast(ushort)(components.length - 1); - char[] name = Mallocator.makeArray(cast(char[]) Comp.stringof); - /*char[] name = Mallocator.makeArray!char(Comp.stringof.length); - name[0..$] = Comp.stringof;*/ + char[] name = Mallocator.makeArray(cast(char[]) ComponentName); components_map.add(name, cast(ushort)(components.length - 1)); } } @@ -1323,7 +1348,8 @@ export struct EntityManager info.size = Ev.sizeof; info.alignment = Ev.alignof; - ushort event_id = events_map.get(Ev.stringof, ushort.max); + //ushort event_id = events_map.get(Ev.stringof, ushort.max); + ushort event_id = events_map.get(fullName!Ev, ushort.max); if (event_id < events.length) { Ev.event_id = event_id; @@ -1332,26 +1358,31 @@ export struct EntityManager { events.add(info); Ev.event_id = cast(ushort)(events.length - 1); - events_map.add(Ev.stringof, cast(ushort)(events.length - 1)); + // events_map.add(Ev.stringof, cast(ushort)(events.length - 1)); + events_map.add(fullName!Ev, cast(ushort)(events.length - 1)); } } export void callEntitiesFunction(Sys, T)(T func) { + //TODO: check if onUpdate function is good Sys* s; static assert(isDelegate!func, "Function must be delegate."); static assert(__traits(hasMember, Sys, "EntitiesData"), "Can't call function with system which hasn't EntitesData structure."); + ///TODO: make possibly to call function to group without system with onUpdate function static assert(__traits(hasMember, Sys, "onUpdate"), "Can't call function with system which hasn't onUpdate function callback."); - static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate), - functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), - "Function must match system update function."); + // static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate), + // functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), + // "Function must match system update function."); FIXME: It's lead to crash on android build static assert(__traits(hasMember, Sys, "system_id"), "Sys must be system type."); System* system = getSystem(Sys.system_id); assert(system != null, "System must be registered in EntityManager before any funcion can be called."); + if (!system.m_any_system_caller) + return; foreach (info; system.m_any_system_caller.infos) { @@ -1479,21 +1510,28 @@ export struct EntityManager if (first_block is null || blocks_count == 0) continue; + //if this info will fill job if ((blocks_count - 1) * info.max_entities + entities_count + info.last_block.entities_count - first_elem >= entities_per_job) { int reamaining_entities = (entities_per_job - entities_count - ( first_block.entities_count - first_elem)); - if (reamaining_entities >= 0) + + //if first block don't fill job + if (reamaining_entities > 0) { + //take as many full blocks as possible int full_blocks_count = reamaining_entities / info.max_entities; EntitiesBlock* block = first_block; foreach (i; 0 .. full_blocks_count + 1) block = block.next_block; + //if full block + actual contained entities + remaining entities form first block > entities count per job if (full_blocks_count * info.max_entities + entities_count + ( first_block.entities_count - first_elem) >= entities_per_job) { + assert(entities_per_job == full_blocks_count * info.max_entities + entities_count + ( + first_block.entities_count - first_elem)); CallData data = CallData(caller.system_id, sys, info, sys.m_update_delegate, first_block, cast(ushort)(full_blocks_count + 1), @@ -1508,6 +1546,8 @@ export struct EntityManager entities_count += full_blocks_count * info.max_entities + ( first_block.entities_count - first_elem); // - first_elem; uint last_elem = entities_per_job - entities_count; // + first_elem - 1; + assert(last_elem > 0); + assert(last_elem <= block.entities_count); CallData data = CallData(caller.system_id, sys, info, sys.m_update_delegate, first_block, cast(ushort)(full_blocks_count + 2), @@ -1515,19 +1555,31 @@ export struct EntityManager tmp_datas.add(data); first_elem = last_elem; blocks_count -= full_blocks_count + 1; - assert(first_elem <= block.entities_count); first_block = block; + if (last_elem == block.entities_count) + { + assert(block.next_block == null); + first_block = null; + } } } else { uint last_elem = entities_per_job - entities_count; + assert(last_elem > 0); CallData data = CallData(caller.system_id, sys, info, sys.m_update_delegate, first_block, 1, cast(ushort) first_elem, cast(ushort)(first_elem + last_elem)); tmp_datas.add(data); first_elem += last_elem; assert(first_elem <= first_block.entities_count); + //if job takes every entity, take next block + if (first_elem == first_block.entities_count) + { + first_elem = 0; + first_block = first_block.next_block; + blocks_count--; + } } nextJob(); entities_count = 0; @@ -1535,6 +1587,7 @@ export struct EntityManager } else { + //take whole info blocks CallData data = CallData(caller.system_id, sys, info, sys.m_update_delegate, first_block, cast(ushort) blocks_count, cast(ushort) first_elem); tmp_datas.add(data); @@ -1828,6 +1881,8 @@ export struct EntityManager foreach (i, id; ids) { + if (current_delta == 0) + current_delta = ushort.max; alignNum(current_delta, components[id].alignment); info.deltas[id] = cast(ushort) current_delta; current_delta += entites_in_block * components[id].size; @@ -2008,6 +2063,9 @@ export struct EntityManager is_: } + ///call Custom Entity Filter test if function exists + if(system.m_filter_entity && !(cast(bool function(void* system_pointer, EntityInfo* info) @nogc nothrow)system.m_filter_entity)(system, &entity))return; + entity.systems[system_id] = true; } @@ -2050,30 +2108,8 @@ export struct EntityManager { System* system = &systems[system_id]; - if (system.m_excluded_components) - { - foreach (id; system.m_excluded_components) - { - foreach (id2; info.components) - { - if (id == id2) - return; - } - } - } - - foreach (id; system.m_components) - { - foreach (i2, id2; info.components) - { - if (id2 == id) - goto is_; - } - return; - is_: - } - - info.systems[system_id] = true; + connectListenerToEntityInfo(info, system_id); + if(!info.systems[system_id])return; uint index = 0; for (; index < passes[system.m_pass].system_callers.length; index++) @@ -2486,17 +2522,17 @@ export struct EntityManager { ushort size = components[comp].size; if (size != 0) - memcpy(cast(void*) new_block + info.deltas[comp] + size * new_id, + memcpy(cast(void*) new_block + info.deltas[comp] + new_id * size, cast(void*) block + info.deltas[comp] + size * index, size); if (components[comp].create_callback) { components[comp].create_callback( - cast(void*) block + info.deltas[comp] + new_id * size); + cast(void*) new_block + info.deltas[comp] + new_id * size); } } - if (new_index == 1 && info.update_block == block) + if (new_index == 1 && info.update_block == new_block) threads[threadID].infosToUpdate.add(info); Entity* new_entity = cast(Entity*) start; @@ -2515,43 +2551,6 @@ export struct EntityManager */ export Entity* addEntity(EntityTemplate* tmpl) { - /*EntityInfo* info = tmpl.info; - - ushort index = 0; - EntitiesBlock* block; - do - { - block = findBlockWithFreeSpaceMT(info); - index = block.added_count.atomicOp!"+="(1); - } - while (block.entities_count + index > info.max_entities); - - uint id = (block.entities_count + index - 1); //block.added_count); - - void* data_begin = block.dataBegin(); - void* start = data_begin + EntityID.sizeof * id; - - foreach (comp; info.components) - { - memcpy(cast(void*) block + info.deltas[comp] + components[comp].size * id, - tmpl.entity_data.ptr + info.tmpl_deltas[comp], components[comp].size); - - if (components[comp].create_callback) - { - components[comp].create_callback( - cast(void*) block + info.deltas[comp] + id * components[comp].size); - } - - } - - if (index == 1) - threads[threadID].infosToUpdate.add(block); - - Entity* entity = cast(Entity*) start; - entity.id = id_manager.getNewID(); - id_manager.update(*entity); //entity.updateID(); - - return entity;*/ return addEntity(tmpl, null); } @@ -3100,12 +3099,12 @@ export struct EntityManager swapData(); has_work = false; - // has_work |= updateBlocks(); + has_work |= updateBlocks(); // has_work |= changeEntities(); // has_work |= removeEntities(); has_work |= updateEvents(); - //id_manager.optimize(); + id_manager.optimize(); has_work |= updateBlocks(); has_work |= changeEntities(); has_work |= removeEntities(); @@ -3493,6 +3492,12 @@ export struct EntityManager return new_info; } + export bool hasComponent(ushort component_id) + { + if(component_id >= deltas.length || !deltas[component_id])return false; + return true; + } + export ~this() @nogc nothrow { if (components) @@ -3501,6 +3506,10 @@ export struct EntityManager Mallocator.dispose(deltas); if (tmpl_deltas) Mallocator.dispose(tmpl_deltas); + if (comp_add_info) + Mallocator.dispose(comp_add_info); + if (comp_rem_info) + Mallocator.dispose(comp_rem_info); if (systems) Mallocator.dispose(systems); if (add_listeners) diff --git a/source/bubel/ecs/simple_vector.d b/source/bubel/ecs/simple_vector.d index bb4b610..acf01ee 100644 --- a/source/bubel/ecs/simple_vector.d +++ b/source/bubel/ecs/simple_vector.d @@ -12,6 +12,12 @@ struct SimpleVector { @disable this(this); + + ~this() nothrow @nogc + { + if(data) + Mallocator.dispose(data); + } ///Add element to vector void add(ubyte el) nothrow @nogc diff --git a/source/bubel/ecs/std.d b/source/bubel/ecs/std.d index a7dd846..d99a05c 100644 --- a/source/bubel/ecs/std.d +++ b/source/bubel/ecs/std.d @@ -112,7 +112,7 @@ else version (D_BetterC) { private const uint max_alloca = 10000; private __gshared byte[max_alloca] alloca_array; - private uint alloca_pos = 0; + private __gshared uint alloca_pos = 0; export extern (C) void* __alloca(size_t length) @nogc nothrow { if (alloca_pos + length > max_alloca) @@ -152,13 +152,86 @@ else static struct Mallocator { + static T[] resizeArray(T)(T[] array, size_t length) nothrow @nogc + { + T[] ret; + + if (length > array.length) + { + ret = (cast(T*) realloc(array.ptr, T.sizeof * length))[0 .. length]; + static if (__traits(isPOD, T)) + { + __gshared immutable T init = T.init; + + foreach (i; array.length .. ret.length) + { + memcpy(&ret[i], &init, T.sizeof); + } + } + else + { + static import std.conv; + + foreach (i; array.length .. ret.length) + { + std.conv.emplace(&ret[i]); + } + } + } + else + { + static if (__traits(hasMember, T, "__xdtor")) + { + foreach (i; length .. array.length) + { + array[i].__xdtor(); + } + } + else static if (__traits(hasMember, T, "__dtor")) + { + foreach (i; length .. array.length) + { + array[i].__dtor(); + } + } + ret = (cast(T*) realloc(array.ptr, T.sizeof * length))[0 .. length]; + } + + return ret; + } + static T[] makeArray(T)(size_t length) nothrow @nogc { T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length]; static if (__traits(isPOD, T)) { - static immutable T init = T.init; + __gshared immutable T init = T.init; + + foreach (i; 0 .. ret.length) + { + memcpy(&ret[i], &init, T.sizeof); + } + } + else + { + static import std.conv; + + foreach (i; 0 .. ret.length) + { + std.conv.emplace(&ret[i]); + } + } + return ret; + } + + static T[] alignMakeArray(T)(size_t length, size_t alignment) nothrow @nogc + { + T[] ret = (cast(T*) alignAlloc(T.sizeof * length, alignment))[0 .. length]; + + static if (__traits(isPOD, T)) + { + __gshared immutable T init = T.init; foreach (i; 0 .. ret.length) { @@ -206,7 +279,7 @@ static struct Mallocator static if (__traits(isPOD, T)) { - static immutable T init = T.init; + __gshared immutable T init = T.init; memcpy(ret, &init, T.sizeof); } else static if (is(T == struct)) @@ -228,13 +301,34 @@ static struct Mallocator return ret; } - static void dispose(T)(T object) nothrow @nogc + static void dispose(T)(T object) { - static if (__traits(hasMember, T, "__xdtor")) - object.__xdtor(); - else static if (__traits(hasMember, T, "__dtor")) - object.__dtor(); - free(cast(void*) object); + static if (isArray!T) + { + alias TT = PointerTarget!(typeof(object.ptr)); + static if (!isPointer!TT) + { + static if (__traits(hasMember, TT, "__xdtor")) + { + foreach (ref TT t; object) + t.__xdtor(); + } + else static if (__traits(hasMember, TT, "__dtor")) + { + foreach (TT t; object) + t.__dtor(); + } + } + free(cast(void*) object.ptr); + } + else + { + static if (__traits(hasMember, T, "__xdtor")) + object.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + object.__dtor(); + free(cast(void*) object); + } } static void alignDispose(T)(T object) diff --git a/source/bubel/ecs/system.d b/source/bubel/ecs/system.d index 7bcaf01..e571e8b 100644 --- a/source/bubel/ecs/system.d +++ b/source/bubel/ecs/system.d @@ -91,6 +91,38 @@ struct System package: + void destroy() + { + import bubel.ecs.std : Mallocator; + disable(); + if (m_destroy) + (cast(void function(void*)) m_destroy)(m_system_pointer); + + if (m_name) + Mallocator.dispose(m_name); + if (m_components) + Mallocator.dispose(m_components); + if (m_excluded_components) + Mallocator.dispose(m_excluded_components); + if (m_optional_components) + Mallocator.dispose(m_optional_components); + if (jobs) + Mallocator.dispose(jobs); + if (m_read_only_components) + Mallocator.dispose(m_read_only_components); + if (m_writable_components) + Mallocator.dispose(m_writable_components); + if (m_readonly_dependencies) + Mallocator.dispose(m_readonly_dependencies); + if (m_writable_dependencies) + Mallocator.dispose(m_writable_dependencies); + if (m_event_callers) + Mallocator.dispose(m_event_callers); + + if (m_system_pointer) + Mallocator.dispose(m_system_pointer); + } + struct EventCaller { ushort id; @@ -162,6 +194,8 @@ package: void* m_remove_entity; void* m_change_entity; + void* m_filter_entity; + //void function(ref EntityManager.CallData data) m_initialize; //void function(ref EntityManager.CallData data) m_deinitilize; void* m_initialize; diff --git a/source/bubel/ecs/traits.d b/source/bubel/ecs/traits.d index 7043b2e..8214b01 100644 --- a/source/bubel/ecs/traits.d +++ b/source/bubel/ecs/traits.d @@ -37,3 +37,35 @@ static long getIndexOfTypeInEntitiesData(EntitiesData, Type)() } return index; } + +static string attachParentName(alias T, string str)() +{ + alias parent = __traits(parent, T); + enum parent_str = parent.stringof; + static if(parent_str[0..7] == "module ") + { + static if(__traits(compiles, __traits(parent, parent))) + { + return attachParentName!(parent, parent_str[7 .. $] ~ '.' ~ str); + } + else return parent_str[8 .. $] ~ '.' ~ str; + } + else static if(parent_str[0..8] == "package ") + { + static if(__traits(compiles, __traits(parent, parent))) + { + return attachParentName!(parent, parent_str[8 .. $] ~ '.' ~ str); + } + else return parent_str[8 .. $] ~ '.' ~ str; + } + else static if(__traits(compiles, __traits(parent, parent))) + { + return attachParentName!(parent, parent_str ~ '.' ~ str); + } + else return parent_str ~ '.' ~ str; +} + +static string fullName(T)() +{ + return attachParentName!(T, T.stringof); +} \ No newline at end of file diff --git a/tests/basic.d b/tests/basic.d index 5046dff..bcea918 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -139,6 +139,7 @@ void afterEveryTest() unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); Entity* entity = gEM.addEntity(tmpl_); EntityMeta meta = entity.getMeta(); assert(meta.hasComponent(CInt.component_id)); @@ -157,6 +158,7 @@ unittest unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); assert(tmpl_.info.components.length == 3); assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof)); assert(tmpl_.getComponent!CInt); @@ -345,6 +347,7 @@ unittest assert(*tmpl_7.getComponent!CLong == 10); gEM.freeTemplate(tmpl_d); + gEM.freeTemplate(tmpl_cp); gEM.freeTemplate(tmpl_); gEM.freeTemplate(tmpl_2); gEM.freeTemplate(tmpl_3); @@ -368,6 +371,8 @@ unittest assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); assert(tmpl_.info == tmpl_2.info); + gEM.freeTemplate(tmpl_); + gEM.freeTemplate(tmpl_2); } @("MultiRegister") @@ -401,7 +406,7 @@ unittest System* ecs_system = gEM.getSystem(EmptySystem.system_id); assert(ecs_system !is null); assert(ecs_system.id == EmptySystem.system_id); - assert(ecs_system.name == "EmptySystem"); + assert(ecs_system.name == "tests.basic.EmptySystem"); gEM.begin(); @@ -605,7 +610,7 @@ unittest assert(ecs_system !is null); assert(ecs_system.id == LongAddSystem.system_id); assert(ecs_system.priority == -1); - assert(ecs_system.name == "LongAddSystem"); + assert(ecs_system.name == "tests.basic.LongAddSystem"); ushort[1] ids = [CLong.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); @@ -897,7 +902,7 @@ unittest { struct TestSystem { - mixin ECS.System; + mixin ECS.System!64; struct EntitiesData { @@ -1003,15 +1008,51 @@ unittest assert(empty_system.update == 3); system.entities = 0; - foreach(i;0..10000)gEM.addEntity(tmpl); + // foreach(i;0..10000)gEM.addEntity(tmpl); - gEM.begin(); + // gEM.begin(); - gEM.updateMT("custom"); + // gEM.updateMT("custom"); - gEM.end(); + // gEM.end(); - assert(system.entities == 12001); + // assert(system.entities == 12001); + + void clearEntities(TestSystem.EntitiesData data) + { + foreach(i;0..data.length) + { + gEM.removeEntity(data.entity[i].id); + } + } + gEM.callEntitiesFunction!TestSystem(&clearEntities); + gEM.commit(); + + foreach(i;0..2000) + { + gEM.addEntity(tmpl); + + gEM.begin(); + gEM.updateMT("custom"); + gEM.end(); + + assert(system.entities == i+1); + system.entities = 0; + } + + foreach(i;0..90000)gEM.addEntity(tmpl); + + foreach(i;0..2000) + { + gEM.addEntity(tmpl); + + gEM.begin(); + gEM.updateMT("custom"); + gEM.end(); + + assert(system.entities == i+92001); + system.entities = 0; + } } unittest @@ -1579,4 +1620,107 @@ unittest assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); assert(pass.system_callers[4].dependencies[0].system_id == TestSystem2.system_id); assert(pass.system_callers[4].dependencies[1].system_id == TestSystem4.system_id); -} \ No newline at end of file +} + + +@("CustomFilter") +unittest +{ + struct TestSystem + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @optional CInt[] int_; + @optional CLong[] long_; + @optional CFloat[] float_; + @optional CDouble[] double_; + } + + bool filterEntity(EntityManager.EntityInfo* info) + { + if(!info.hasComponent(CInt.component_id))return false; + int one_from = 0; + if(info.hasComponent(CLong.component_id))one_from++; + if(info.hasComponent(CFloat.component_id))one_from++; + if(info.hasComponent(CDouble.component_id))one_from++; + if(one_from == 1)return true; + return false; + } + + void onUpdate(EntitiesData entities) + { + updates++; + } + + uint updates = 0; + } + + struct TestSystem2 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @optional CInt[] int_; + @optional CLong[] long_; + @optional CFloat[] float_; + @optional CDouble[] double_; + } + + bool filterEntity(EntityManager.EntityInfo* info) + { + if(info.hasComponent(CInt.component_id) && info.hasComponent(CFloat.component_id) && !info.hasComponent(CLong.component_id) && !info.hasComponent(CDouble.component_id))return true; + if(info.hasComponent(CLong.component_id) && info.hasComponent(CDouble.component_id) && !info.hasComponent(CInt.component_id) && !info.hasComponent(CFloat.component_id))return true; + return false; + } + + void onUpdate(EntitiesData entities) + { + updates++; + } + + uint updates = 0; + } + + gEM.beginRegister(); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + + gEM.endRegister(); + + + EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CLong.component_id, CFloat.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); + EntityTemplate* tmpl_2 = gEM.allocateTemplate([CInt.component_id, CFloat.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_2); + EntityTemplate* tmpl_3 = gEM.allocateTemplate([CLong.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_3); + EntityTemplate* tmpl_4 = gEM.allocateTemplate([CInt.component_id, CLong.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_4); + EntityTemplate* tmpl_5 = gEM.allocateTemplate([CInt.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_5); + EntityTemplate* tmpl_6 = gEM.allocateTemplate([CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_6); + + gEM.addEntity(tmpl_); + gEM.addEntity(tmpl_2); + gEM.addEntity(tmpl_3); + gEM.addEntity(tmpl_4); + gEM.addEntity(tmpl_5); + gEM.addEntity(tmpl_6); + + TestSystem* test_system = gEM.getSystem!TestSystem; + TestSystem2* test_system2 = gEM.getSystem!TestSystem2; + + gEM.begin(); + gEM.update(); + gEM.end(); + + assert(test_system.updates == 2); + assert(test_system2.updates == 2); +} diff --git a/tests/runner.d b/tests/runner.d index 7e3dd07..83c2288 100644 --- a/tests/runner.d +++ b/tests/runner.d @@ -236,7 +236,7 @@ struct TestRunner(Args...) } else { - import core.exception : AssertError; + import core.exception : AssertError, RangeError; try { unittest_(); @@ -249,6 +249,13 @@ struct TestRunner(Args...) test.file_line = cast(int)error.line; test.msg = copyString(error.msg); } + catch(RangeError error) + { + test.passed = false; + test.file = copyString(error.file); + test.file_line = cast(int)error.line; + test.msg = copyString(error.msg); + } } if (test.passed) diff --git a/tests/tests.d b/tests/tests.d index d923116..d539f89 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -93,6 +93,13 @@ struct TestEvent2 float a; } +static struct CPosition +{ + mixin ECS.Component; + float x; + float y; +} + static struct TestComp { mixin ECS.Component; //__gshared ushort component_id; @@ -186,6 +193,52 @@ static struct TestComp5 } } +struct EverySystem +{ + mixin ECS.System; + + struct EntitiesData + { + uint length; + Entity[] entity; + CPosition[] pos; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.pos[i].x++; + data.pos[i].y++; + } + } + + void iterate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.pos[i].x++; + data.pos[i].y++; + } + } + + void free(EntitiesData data) + { + foreach(i;0..data.length) + { + gEM.removeEntity(data.entity[i].id); + } + } + + void addOne(EntitiesData data) + { + foreach(i;0..data.length) + { + gEM.addComponents(data.entity[i].id, TestComp2()); + } + } +} + struct ChangeTestSystem { mixin ECS.System!16; //__gshared ushort system_id; @@ -648,6 +701,7 @@ else: gEM.registerComponent!TestComp; gEM.registerComponent!TestComp3; gEM.registerComponent!TestComp5; + gEM.registerComponent!CPosition; gEM.registerEvent!TestEvent; gEM.registerEvent!TestEvent2; @@ -669,6 +723,7 @@ else: gEM.registerSystem!EmptySystem(2); gEM.registerSystem!EmptyEventSystem(2); gEM.registerSystem!EventSystem(2); + gEM.registerSystem!EverySystem(0); //gEM.registerSystem!TestSystemWithHighPriority(100); //gEM.registerSystem!TestSystem2(0); gEM.endRegister(); @@ -693,6 +748,62 @@ else: //dur = (MonoTime.currTime - time).total!"usecs"; //writeln("Template allocating: ", dur, " usecs"); printf("Template allocating: %f usecs\n", cast(float)(Time.getUSecTime() - time)); + + + time = Time.getUSecTime(); + ushort[1] empty_ids = [CPosition.component_id]; + EntityTemplate* tmpl_empty = gEM.allocateTemplate(empty_ids); + + gEM.commit(); + + time = Time.getUSecTime(); + + foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty); + gEM.commit(); + foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty); + gEM.commit(); + foreach(i;0..2_000_000)gEM.addEntity(tmpl_empty); + gEM.commit(); + + printf("Adding 1M entities: %f usecs\n", cast(float)(Time.getUSecTime() - time)); + + gEM.commit(); + time = Time.getUSecTime(); + gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().iterate); + printf("Iterate 1M entities: %f usecs\n", cast(float)(Time.getUSecTime() - time)); + + gEM.begin(); + time = Time.getUSecTime(); + gEM.update(); + printf("Iterate 1M entities (update): %f usecs\n", cast(float)(Time.getUSecTime() - time)); + gEM.end(); + + time = Time.getUSecTime(); + gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().free); + gEM.commit(); + printf("Deleting 1M entities: %f usecs\n", cast(float)(Time.getUSecTime() - time)); + + time = Time.getUSecTime(); + + foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty); + gEM.commit(); + foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty); + gEM.commit(); + foreach(i;0..2_000_000)gEM.addEntity(tmpl_empty); + gEM.commit(); + + printf("Adding 1M entities (prealloc): %f usecs\n", cast(float)(Time.getUSecTime() - time)); + + gEM.commit(); + time = Time.getUSecTime(); + gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().addOne); + gEM.commit(); + printf("Adding 1M component: %f usecs\n", cast(float)(Time.getUSecTime() - time)); + + gEM.commit(); + gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().free); + gEM.commit(); + time = Time.getUSecTime(); EntityID entity; @@ -903,6 +1014,7 @@ else: writeEntityComponents(gEM.getEntity(entity)); //import std.stdio; ////writeln((cast(uint*)tmpl.info.first_block)[0..48]); + gEM.freeTemplate(tmpl_empty); gEM.freeTemplate(tmpl); gEM.freeTemplate(tmpl2); gEM.freeTemplate(copy_tempalte);