# HG changeset patch # User Sam # Date 1683733019 -25200 # Node ID b5d9410a818408d6b926be2c6959aa76fd2ea3c8 # Parent 77755701bf49541787198c013ffe37b9a1f8c331 add: resource packaging and loading for different resource types, simplify build commands, update readme diff -r 77755701bf49 -r b5d9410a8184 README.md --- a/README.md Tue May 09 20:39:49 2023 +0700 +++ b/README.md Wed May 10 22:36:59 2023 +0700 @@ -3,8 +3,9 @@ Hi there -This is a very simplistic little game engine, mainly trying to wrap around vulkan and the operating system's windowing, input and audio system. -I am using the last programming language you will ever need, [Nim](https://nim-lang.org/) +This is a little game engine, mainly trying to wrap around vulkan and the +operating system's windowing, input and audio system. I am using the last +programming language you will ever need, [Nim](https://nim-lang.org/) Building -------- @@ -42,7 +43,11 @@ - [ ] Mipmaps Asset handling: -- [ ] Resource concept: load from directory, zip or in-memory-zip, select "mod" as root +- [ ] Resource concept (TODO: test on windows) + - [x] Mod/resource-pack concept + - [x] Load from directory + - [x] Load from zip + - [x] Load from exe-embeded - [ ] Mesh files (Wavefront OBJ, MTL) (use something from sketchfab for testing, https://sketchfab.com/) - [ ] Image files (BMP RGB + BMP Graysscale for transparency) - [ ] Audio files (AU) diff -r 77755701bf49 -r b5d9410a8184 config.nims --- a/config.nims Tue May 09 20:39:49 2023 +0700 +++ b/config.nims Wed May 10 22:36:59 2023 +0700 @@ -8,91 +8,62 @@ const LINUX = "linux" const WINDOWS = "windows" -proc compilerFlags() = +const BUNDLETYPE* {.strdefine.}: string = "dir" # dir, zip, exe +const RESOURCEROOT* {.strdefine.}: string = "resources" + +task build, "build": switch("path", "src") switch("mm", "orc") switch("experimental", "strictEffects") switch("threads", "on") + var buildType = DEBUG + var platformDir = "" if defined(linux): switch("define", "VK_USE_PLATFORM_XLIB_KHR") + platformDir = LINUX if defined(windows): switch("define", "VK_USE_PLATFORM_WIN32_KHR") - - -proc compilerFlagsDebug() = - switch("debugger", "native") - -proc compilerFlagsRelease() = - switch("define", "release") - switch("app", "gui") - -task single_linux_debug, "build linux debug": - compilerFlags() - compilerFlagsDebug() - switch("outdir", BUILDBASE / DEBUG / LINUX) - setCommand "c" - mkDir(BUILDBASE / DEBUG / LINUX) + platformDir = WINDOWS + if defined(release): + switch("app", "gui") + buildType = RELEASE + else: + switch("debugger", "native") -task single_linux_release, "build linux release": - compilerFlags() - compilerFlagsRelease() - switch("outdir", BUILDBASE / RELEASE / LINUX) + var outdir = getCurrentDir() / BUILDBASE / buildType / platformDir / projectName() + switch("outdir", outdir) setCommand "c" - mkDir(BUILDBASE / RELEASE / LINUX) + rmDir(outdir) + mkDir(outdir) + let resourcedir = joinPath(projectDir(), RESOURCEROOT) + if existsDir(resourcedir): + let outdir_resources = joinPath(outdir, RESOURCEROOT) + if BUNDLETYPE == "dir": + cpDir(resourcedir, outdir_resources) + elif BUNDLETYPE == "zip": + mkDir(outdir_resources) + for resource in listDirs(resourcedir): + let + oldcwd = getCurrentDir() + outputfile = joinPath(outdir_resources, resource.splitPath().tail & ".zip") + inputfile = resource.splitPath().tail + cd(resource) + if defined(linux): + exec &"zip -r {outputfile} ." + elif defined(windows): + # TODO: test this + exec &"powershell Compress-Archive {inputfile} {outputfile}" + cd(oldcwd) -task single_windows_debug, "build windows debug": - compilerFlags() - compilerFlagsDebug() - switch("outdir", BUILDBASE & "/" & DEBUG & "/" & WINDOWS) - setCommand "c" - mkDir(BUILDBASE & "/" & DEBUG & "/" & WINDOWS) - -task single_windows_release, "build windows release": - compilerFlags() - compilerFlagsRelease() - switch("outdir", BUILDBASE & "/" & RELEASE & "/" & WINDOWS) - setCommand "c" - mkDir(BUILDBASE & "/" & RELEASE & "/" & WINDOWS) - -task build_all_linux_debug, "build all examples with linux/debug": +task build_all_debug, "build all examples for debug": for file in listFiles("examples"): if file.endsWith(".nim"): - selfExec(&"single_linux_debug {file}") - -task build_all_linux_release, "build all examples with linux/release": - for file in listFiles("examples"): - if file.endsWith(".nim"): - selfExec(&"single_linux_release {file}") + exec(&"nim build {file}") -task build_all_windows_debug, "build all examples with windows/debug": - for file in listFiles("examples"): - if file.endsWith(".nim"): - exec(&"nim single_windows_debug --define:mingw {file}") - -task build_all_windows_release, "build all examples with windows/release": +task build_all_release, "build all examples for release": for file in listFiles("examples"): if file.endsWith(".nim"): - exec(&"nim single_windows_release --define:mingw {file}") - -task build_all_debug, "build all examples with */debug": - build_all_linux_debugTask() - build_all_windows_debugTask() - -task build_all_release, "build all examples with */release": - build_all_linux_releaseTask() - build_all_windows_releaseTask() - -task build_all_linux, "build all examples with linux/*": - build_all_linux_debugTask() - build_all_linux_releaseTask() - -task build_all_windows, "build all examples with windows/*": - build_all_windows_debugTask() - build_all_windows_releaseTask() - -task build_all, "build all examples": - build_all_linuxTask() - build_all_windowsTask() + exec(&"nim build -d:release {file}") task clean, "remove all build files": exec(&"rm -rf {BUILDBASE}") @@ -116,6 +87,7 @@ exec &"rm -rf {dirname}" task glslangValidator_exe, "Download glslangValidator.exe (required for windows compilation)": + # TODO: make this work on windows let dirname = "/tmp/glslang_download" exec &"mkdir -p {dirname}" exec &"cd {dirname} && wget https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip" @@ -144,4 +116,10 @@ cpDir "src/vulkan_api/output/platform", "src/semicongine/vulkan/platform" if getCommand() in ["c", "compile", "r", "dump", "check", "idetools"]: - compilerFlags() + --path:src + --mm:orc + --threads:on + if defined(linux): + --d:VK_USE_PLATFORM_XLIB_KHR + if defined(windows): + --d:VK_USE_PLATFORM_WIN32_KHR diff -r 77755701bf49 -r b5d9410a8184 semicongine.nimble --- a/semicongine.nimble Tue May 09 20:39:49 2023 +0700 +++ b/semicongine.nimble Wed May 10 22:36:59 2023 +0700 @@ -12,3 +12,4 @@ requires "nim >= 1.6.10" requires "winim" requires "xlib" +requires "zippy" diff -r 77755701bf49 -r b5d9410a8184 src/semicongine/core/buildconfig.nim --- a/src/semicongine/core/buildconfig.nim Tue May 09 20:39:49 2023 +0700 +++ b/src/semicongine/core/buildconfig.nim Wed May 10 22:36:59 2023 +0700 @@ -52,5 +52,5 @@ const LOGLEVEL {.strdefine.}: string = (when DEBUG: "lvlAll" else: "lvlWarn") const ENGINE_LOGLEVEL* = parseEnum[Level](LOGLEVEL) -const MODROOT* {.strdefine.}: string = "mods" -const BUNDLETYPE* {.strdefine.}: string = "Dir" # zip, exe +const RESOURCEROOT* {.strdefine.}: string = "resources" +const BUNDLETYPE* {.strdefine.}: string = "dir" # dir, zip, exe diff -r 77755701bf49 -r b5d9410a8184 src/semicongine/resources.nim --- a/src/semicongine/resources.nim Tue May 09 20:39:49 2023 +0700 +++ b/src/semicongine/resources.nim Wed May 10 22:36:59 2023 +0700 @@ -4,42 +4,97 @@ import ./core type - ResourceBundlingType* = enum + Binary* = seq[uint8] + ResourceBundlingType = enum Dir # Directories Zip # Zip files Exe # Embeded in executable - Resource = ref object of RootObj - name: string - data: seq[uint8] + +const thebundletype = parseEnum[ResourceBundlingType](BUNDLETYPE.toLowerAscii().capitalizeAscii()) +var selectedMod* = "default" + +# resource loading + +when thebundletype == Dir: + + proc resourceRoot(): string = + joinPath(absolutePath(getAppDir()), RESOURCEROOT) + proc modRoot(): string = + joinPath(resourceRoot(), selectedMod) + + proc loadResource_intern(path: string): Binary = + cast[Binary](readFile(joinPath(modRoot(), path))) + + proc modList_intern(): seq[string] = + for kind, file in walkDir(resourceRoot(), relative=true): + if kind == pcDir: + result.add file -const thebundletype = parseEnum[ResourceBundlingType](BUNDLETYPE) + iterator walkResources_intern(): string = + for i in walkDir(modRoot(), relative=true): + yield i.path + +elif thebundletype == Zip: + + import zippy/ziparchives + + proc resourceRoot(): string = + joinPath(absolutePath(getAppDir()), RESOURCEROOT) + proc modRoot(): string = + joinPath(resourceRoot(), selectedMod) + + proc loadResource_intern(path: string): Binary = + let reader = openZipArchive(modRoot() & ".zip") + result = cast[Binary](reader.extractFile(path)) + reader.close() + + proc modList_intern(): seq[string] = + for kind, file in walkDir(resourceRoot(), relative=true): + if kind == pcFile and file.endsWith(".zip"): + result.add file[0 ..< ^4] -proc loadResourceDirectory(path: string): Resource = - var fullpath = joinPath(MODROOT, path) - result -proc loadResourceZip(path: string): Resource = - var fullpath = joinPath(MODROOT, path) - result -proc loadResourceExecutable(path: string): Resource = - result -proc loadResource*(path: string): Resource = - case thebundletype - of Dir: return loadResourceDirectory(path) - of Zip: return loadResourceZip(path) - of Exe: return loadResourceExecutable(path) + iterator walkResources_intern(): string = + let reader = openZipArchive(modRoot() & ".zip") + for i in reader.walkFiles: + yield i.splitPath().tail + reader.close() + +elif thebundletype == Exe: + + import std/compilesettings + import std/tables + import std/sequtils + + proc loadResources(): Table[string, Table[string, string]] {.compileTime.} = -proc bundleResourcesDirectory() {.compiletime.} = - # copy resource files to output directory - discard -proc bundleResourcesZip() {.compiletime.} = - # copy resource files to zip in output directory - discard -proc bundleResourcesExecutable() {.compiletime.} = - # put resource contents into variable here - discard -proc bundleResources() {.compiletime.} = - when thebundletype == Dir: bundleResourcesDirectory() - elif thebundletype == Zip: bundleResourcesZip() - elif thebundletype == Exe: bundleResourcesExecutable() + let srcdir = joinPath(parentDir(querySetting(projectFull)), RESOURCEROOT) + for kind, moddir in walkDir(srcdir): + if kind == pcDir: + let modname = moddir.splitPath.tail + result[modname] = Table[string, string]() + for resourcefile in walkDirRec(moddir, relative=true): + # TODO: add Lempel–Ziv–Welch compression or something similar simple + result[modname][resourcefile] = staticRead(joinPath(moddir, resourcefile)) + const bundledResources = loadResources() + + proc loadResource_intern(path: string): Binary = + # TODO: add Lempel–Ziv–Welch compression or something similar simple + cast[seq[uint8]](bundledResources[selectedMod][path]) -static: bundleResources() + proc modList_intern(): seq[string] = + result = bundledResources.keys().toSeq() + + iterator walkResources_intern(): string = + for i in bundledResources[selectedMod].keys: + yield i + +proc loadResource*(path: string): ref Binary = + result = new Binary + result[] = loadResource_intern(path) + +proc modList*(): seq[string] = + modList_intern() + +iterator walkResources*(): string = + for i in walkResources_intern(): + yield i diff -r 77755701bf49 -r b5d9410a8184 tests/test_resources.nim --- a/tests/test_resources.nim Tue May 09 20:39:49 2023 +0700 +++ b/tests/test_resources.nim Wed May 10 22:36:59 2023 +0700 @@ -3,30 +3,12 @@ import semicongine proc main() = - var scene = newScene("main", root=newEntity("rect", rect())) - var engine = initEngine("Test materials") - const - vertexInput = @[ - attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead), - ] - fragOutput = @[attr[Vec4f]("color")] - vertexCode = compileGlslShader( - stage=VK_SHADER_STAGE_VERTEX_BIT, - inputs=vertexInput, - main="""gl_Position = vec4(position, 1.0);""" - ) - fragmentCode = compileGlslShader( - stage=VK_SHADER_STAGE_FRAGMENT_BIT, - outputs=fragOutput, - main="""color = vec4(1, 0, 0, 1);""" - ) - engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode)) - engine.addScene(scene, vertexInput) - var t = cpuTime() - while engine.updateInputs() == Running and not engine.keyIsDown(Escape): - engine.renderScene(scene) - engine.destroy() - + echo "Mods available: ", modList() + for modName in modList(): + echo modName, ":" + selectedMod = modName + for i in walkResources(): + echo " ", i, ": ", loadResource(i)[] when isMainModule: main()