Utils example

This commit is contained in:
2021-10-17 11:08:40 -07:00
parent b2e99bb976
commit 3fd0efb4d8
12 changed files with 272 additions and 58 deletions

View File

@ -15,24 +15,29 @@ add_compile_definitions(
)
# Do initial set up depending on the build target type.
if(TARGET_TYPE STREQUAL tool)
set(TARGET_NAME tool)
elseif(TARGET_TYPE STREQUAL test)
if(TARGET_TYPE STREQUAL test)
set(TARGET_NAME test)
else()
elseif(TARGET_TYPE STREQUAL game)
set(TARGET_NAME ${TARGET_GAME})
else()
message(FATAL_ERROR "Missing or invalid definition of TARGET_TYPE")
endif()
# Set up the project
project(${TARGET_NAME} VERSION 1.0)
add_executable(${PROJECT_NAME})
# Now change sources depending on the target type
if(TARGET_TYPE STREQUAL tool)
# Variables
SET(ROOT_DIR "${CMAKE_SOURCE_DIR}")
set(TOOLS_DIR "${ROOT_DIR}/tools")
elseif(TARGET_TYPE STREQUAL test)
# Include tools
add_subdirectory(tools)
# Now change sources depending on the target type
if(TARGET_TYPE STREQUAL test)
add_subdirectory(test)
else()
elseif(TARGET_TYPE STREQUAL game)
if(TARGET_GAME STREQUAL poker)
add_compile_definitions(
GAME_NAME="Penny's Poker"
@ -43,8 +48,13 @@ else()
GAME_DISPOSE=pokerGameDispose
GAME_VERSION=1.0
)
endif()
set(DIR_CHARS assets/poker/characters/penny)
tool_vn_character(penny ${DIR_CHARS}/character.xml ${DIR_CHARS}/bruh.png)
add_dependencies(${PROJECT_NAME} penny)
endif()
add_subdirectory(client)
endif()

View File

@ -9,7 +9,13 @@
void engineInit(engine_t *engine) {
randSeed(123);
engine->name = GAME_NAME;
#if defined(GAME_NAME)
engine->name = GAME_NAME;
#else
engine->name = "Dawn";
#endif
clientInit(&engine->client);
epochInit(&engine->time);
renderInit();

View File

@ -11,7 +11,7 @@
file(GLOB GAME_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB GAME_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_GAME}/*.c)
file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_GAME}*.h)
file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_GAME}/*.h)
target_sources(${PROJECT_NAME}
PRIVATE

6
tools/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
# Copyright (c) 2021 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(vn)

12
tools/utils/args.js Normal file
View File

@ -0,0 +1,12 @@
const args = process.argv.splice(2).reduce((x,y) => {
const bits = y.split('=');
const name = bits[0].replace('--', '');
const val = bits[1];
x[name] = val;
return x;
}, {});
module.exports = {
args
};

19
tools/utils/file.js Normal file
View File

@ -0,0 +1,19 @@
const path = require('path');
const fs = require('fs');
const mkdirp = dir => {
const resolved = path.resolve(dir);
const resolvedDir = path.dirname(resolved);
const bits = resolvedDir.split(path.sep);
let running = '';
bits.forEach(bit => {
running += bit;
if(!fs.existsSync(running)) fs.mkdirSync(running);
running += path.sep;
});
}
module.exports = {
mkdirp
}

103
tools/utils/image.js Normal file
View File

@ -0,0 +1,103 @@
const { PNG } = require("pngjs");
const path = require('path');
const fs = require('fs');
/**
* Loads an image into memory.
* @param image Image to load
* @returns A promise that resolves to the loaded image.
*/
const imageLoad = (image) => new Promise(resolve => {
fs.createReadStream(image)
.pipe(new PNG({ filterType: 4 }))
.on("parsed", function () {
// Normalize
const pixels = [];
for(let y = 0; y < this.height; y++) {
for(let x = 0; x < this.width; x++) {
const idx = (this.width * y + x) << 2;
const r = this.data[idx];
const g = this.data[idx + 1];
const b = this.data[idx + 2];
const a = this.data[idx + 3];
pixels.push({ r, g, b, a });
}
}
resolve({ pixels, width: this.width, height: this.height });
})
;
});
/**
* Writes an image to an output file.
* @param image Image to write.
* @param file File to write to.
* @returns A promise that, when resolved, has saved the image.
*/
const imageWrite = (image, file) => new Promise(resolve => {
const png = new PNG({ width: image.width, height: image.height });
png.width = image.width;
png.height = image.height;
for(let y = 0; y < image.height; y++) {
for(let x = 0; x < image.width; x++) {
const i = (image.width * y + x);
const idx = i << 2;
const pixel = image.pixels[i];
png.data[idx] = pixel.r;
png.data[idx + 1] = pixel.g;
png.data[idx + 2] = pixel.b;
png.data[idx + 3] = pixel.a;
}
}
png.pack().pipe(fs.createWriteStream(file))
.on('close', () => resolve(true))
;
});
/**
* Creates a blank image
* @param width Width of the image.
* @param height Height of the image.
* @param fill Optional pixel to fill with, defaults to 0,0,0,0
* @returns The newly created image.
*/
const imageCreate = (width, height, pixel) => {
if(!pixel || !pixel.r) pixel = { r:0, g:0, b:0, a:0 };
const pixels = [];
for(let i = 0; i < width * height; i++) pixels.push({ ...pixel });
return { pixels, width, height };
}
/**
* Copies an area of a source image into a target image.
* @param target Target image to copy into.
* @param source Source image to copy from.
* @param tx Target X position to copy into
* @param ty Target Y position to copy into
* @param sub Optional source area to use, defined as { x, y, width, height }.
*/
const imageCopy = (target, source, tx, ty, sub) => {
if(!sub) sub = { x: 0, y: 0, width: source.width, height: source.height };
for(let x = sub.x; x < sub.x+sub.width; x++) {
for(let y = sub.y; y < sub.y+sub.height; y++) {
let absX = x - sub.x + tx;
let absY = y - sub.y + ty;
if(absX > target.width || absY > target.height) continue;
let ti = absY * target.width + absX;
let si = y * source.width + x;
target.pixels[ti] = { ...source.pixels[si] };
}
}
}
module.exports = {
imageWrite,
imageCreate,
imageLoad,
imageCopy
}

12
tools/vn/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
# Copyright (c) 2021 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
function(tool_vn_character DEP_NAME IN OUT)
add_custom_target(${DEP_NAME}
COMMAND node ${TOOLS_DIR}/vn/character-sheet-generator.js --root="${ROOT_DIR}" --in="${ROOT_DIR}/${IN}" --out="${OUT}"
COMMENT "Adding VN Character ${FILE_NAME}"
)
endfunction()

View File

@ -0,0 +1,93 @@
const path = require('path');
const { imageCreate, imageWrite, imageLoad, imageCopy } = require('./../utils/image');
const fs = require('fs');
const xml = require('xml-js');
const { args } = require('./../utils/args');
const { mkdirp } = require('../utils/file');
// Parse Args
if(!args.root) throw new Error(`Missing root argument`);
if(!args.in) throw new Error(`Missing in argument`);
if(!args.out) throw new Error(`Missing out argument`);
if(!args.in.endsWith('xml')) throw new Error(`Invalid in XML`);
if(!args.out.endsWith('png')) throw new Error(`Invalid out PNG`);
// Determine in and out.
const root = path.resolve(args.root);
const file = path.resolve(args.in);
if(!fs.existsSync(file)) throw new Error(`Could not find ${file}`);
const outFile = path.resolve(args.out);
if(fs.existsSync(outFile)) return;
// Load XML
const data = xml.xml2js(fs.readFileSync(file, 'utf-8'));
const [ character ] = data.elements;
// Validate file.
if(!character.attributes.context) throw new Error(`Missing context`)
const dir = path.resolve(root, 'assets', character.attributes.context);
// Parse base and layers
const base = character.elements.find(e => e.name == 'base').attributes;
if(!base) throw new Error(`Failed to find base`);
const layers = character.elements
.filter(e => e.name == 'layer')
.map(e => e.attributes)
.map(e => ({
...e,
x: parseInt(e.x),
y: parseInt(e.y),
width: parseInt(e.width),
height: parseInt(e.height)
}))
;
(async () => {
// Load the base
const baseImage = await imageLoad(path.join(dir, base.file));
let columnsMax = 0;
let widthMax = 0;
layers.forEach((layer,row) => {
if(!layer.width || !layer.height || !layer.x || !layer.y) {
throw new Error(`Missing layer info`);
}
const layerDir = path.join(dir, layer.directory);
const scan = fs.readdirSync(layerDir);
columnsMax = Math.max(scan.length, columnsMax);
widthMax = Math.max(widthMax, layer.width);
});
// Create the output buffer
const out = imageCreate(
baseImage.width + (columnsMax * widthMax),
baseImage.height
);
// Copy the base
imageCopy(out, baseImage, 0, 0);
// Now begin copying the children, row is defined by the directory
let y = 0;
for(let row = 0; row < layers.length; row++) {
const layer = layers[row];
const layerDir = path.join(dir, layer.directory);
const scan = fs.readdirSync(layerDir);
// Column defined by the file index
for(let col = 0; col < scan.length; col++) {
const img = await imageLoad(path.join(layerDir, scan[col]));
console.log('Copying', scan[col]);
imageCopy(out, img,
baseImage.width+(col*layer.width), y,
layer
);
}
y += layer.height;
}
mkdirp(outFile);
await imageWrite(out, outFile);
})().catch(console.error);

View File

@ -1,24 +0,0 @@
# Copyright (c) 2021 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Definitions
# Libraries
# Sources
file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
target_sources(${PROJECT_NAME}
PRIVATE
${SOURCES}
${HEADERS}
)
# Includes
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)

View File

@ -1,12 +0,0 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "character-sheet-maker.h"
int32_t main(int32_t argc, char *argv[]) {
}

View File

@ -1,11 +0,0 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
int32_t main(int32_t argc, char *argv[]);