From 480463b0578407a8f5a6585eb7018933e6ec7186 Mon Sep 17 00:00:00 2001 From: yum Date: Sat, 26 Nov 2022 17:08:09 -0800 Subject: Add emotes Add emotes.py. It accepts a list of images and creates a texture with 64 total embedded images. The shader knows how to draw these into fixed 6-character-wide slots. Each slot must be aligned to a 6-character boundary. osc_ctrl has to pad with spaces to make this work. This whole patch is a little more complicated than it has any right to be, but my brain feels fuzzy and I don't know where to start fixing it, so I'm going to leave it shitty-but-functional for now. There's also some bug where writing a character into the 11th slot causes it to show up at the end of the board. I'll figure that out later, idk. I didn't include any of the emotes I use since I couldn't find any info on their licenses. I'm just banking on having a good workflow later on so people can add their own. --- Shaders/TaSTT.shader | 140 ++++++++++++++++++++++++++++++++++++++++++++------- emotes.py | 123 ++++++++++++++++++++++++++++++++++++++++++++ generate_utils.py | 12 ++--- libtastt.py | 2 +- osc_ctrl.py | 58 ++++++++++++++++----- 5 files changed, 296 insertions(+), 39 deletions(-) create mode 100644 emotes.py diff --git a/Shaders/TaSTT.shader b/Shaders/TaSTT.shader index 79a1bfc..66becaf 100644 --- a/Shaders/TaSTT.shader +++ b/Shaders/TaSTT.shader @@ -9,6 +9,7 @@ _Font_0x8000_0x9FFF ("Font 4 (unicode 0x8000 - 0x9FFFF)", 2D) = "white" {} _Font_0xA000_0xBFFF ("Font 5 (unicode 0xA000 - 0xBFFFF)", 2D) = "white" {} _Font_0xC000_0xDFFF ("Font 6 (unicode 0xC000 - 0xDFFFF)", 2D) = "white" {} + _Img_0xE000_0xE03F ("Images 0", 2D) = "white" {} TaSTT_Backplate("TaSTT_Backplate", 2D) = "black" {} @@ -60,6 +61,10 @@ _Letter_Row00_Col41_Byte0("_Letter_Row00_Col41_Byte0", float) = 0 _Letter_Row00_Col42_Byte0("_Letter_Row00_Col42_Byte0", float) = 0 _Letter_Row00_Col43_Byte0("_Letter_Row00_Col43_Byte0", float) = 0 + _Letter_Row00_Col44_Byte0("_Letter_Row00_Col44_Byte0", float) = 0 + _Letter_Row00_Col45_Byte0("_Letter_Row00_Col45_Byte0", float) = 0 + _Letter_Row00_Col46_Byte0("_Letter_Row00_Col46_Byte0", float) = 0 + _Letter_Row00_Col47_Byte0("_Letter_Row00_Col47_Byte0", float) = 0 _Letter_Row01_Col00_Byte0("_Letter_Row01_Col00_Byte0", float) = 0 _Letter_Row01_Col01_Byte0("_Letter_Row01_Col01_Byte0", float) = 0 _Letter_Row01_Col02_Byte0("_Letter_Row01_Col02_Byte0", float) = 0 @@ -104,6 +109,10 @@ _Letter_Row01_Col41_Byte0("_Letter_Row01_Col41_Byte0", float) = 0 _Letter_Row01_Col42_Byte0("_Letter_Row01_Col42_Byte0", float) = 0 _Letter_Row01_Col43_Byte0("_Letter_Row01_Col43_Byte0", float) = 0 + _Letter_Row01_Col44_Byte0("_Letter_Row01_Col44_Byte0", float) = 0 + _Letter_Row01_Col45_Byte0("_Letter_Row01_Col45_Byte0", float) = 0 + _Letter_Row01_Col46_Byte0("_Letter_Row01_Col46_Byte0", float) = 0 + _Letter_Row01_Col47_Byte0("_Letter_Row01_Col47_Byte0", float) = 0 _Letter_Row02_Col00_Byte0("_Letter_Row02_Col00_Byte0", float) = 0 _Letter_Row02_Col01_Byte0("_Letter_Row02_Col01_Byte0", float) = 0 _Letter_Row02_Col02_Byte0("_Letter_Row02_Col02_Byte0", float) = 0 @@ -148,6 +157,10 @@ _Letter_Row02_Col41_Byte0("_Letter_Row02_Col41_Byte0", float) = 0 _Letter_Row02_Col42_Byte0("_Letter_Row02_Col42_Byte0", float) = 0 _Letter_Row02_Col43_Byte0("_Letter_Row02_Col43_Byte0", float) = 0 + _Letter_Row02_Col44_Byte0("_Letter_Row02_Col44_Byte0", float) = 0 + _Letter_Row02_Col45_Byte0("_Letter_Row02_Col45_Byte0", float) = 0 + _Letter_Row02_Col46_Byte0("_Letter_Row02_Col46_Byte0", float) = 0 + _Letter_Row02_Col47_Byte0("_Letter_Row02_Col47_Byte0", float) = 0 _Letter_Row03_Col00_Byte0("_Letter_Row03_Col00_Byte0", float) = 0 _Letter_Row03_Col01_Byte0("_Letter_Row03_Col01_Byte0", float) = 0 _Letter_Row03_Col02_Byte0("_Letter_Row03_Col02_Byte0", float) = 0 @@ -192,6 +205,10 @@ _Letter_Row03_Col41_Byte0("_Letter_Row03_Col41_Byte0", float) = 0 _Letter_Row03_Col42_Byte0("_Letter_Row03_Col42_Byte0", float) = 0 _Letter_Row03_Col43_Byte0("_Letter_Row03_Col43_Byte0", float) = 0 + _Letter_Row03_Col44_Byte0("_Letter_Row03_Col44_Byte0", float) = 0 + _Letter_Row03_Col45_Byte0("_Letter_Row03_Col45_Byte0", float) = 0 + _Letter_Row03_Col46_Byte0("_Letter_Row03_Col46_Byte0", float) = 0 + _Letter_Row03_Col47_Byte0("_Letter_Row03_Col47_Byte0", float) = 0 _Letter_Row00_Col00_Byte1("_Letter_Row00_Col00_Byte1", float) = 0 _Letter_Row00_Col01_Byte1("_Letter_Row00_Col01_Byte1", float) = 0 _Letter_Row00_Col02_Byte1("_Letter_Row00_Col02_Byte1", float) = 0 @@ -236,6 +253,10 @@ _Letter_Row00_Col41_Byte1("_Letter_Row00_Col41_Byte1", float) = 0 _Letter_Row00_Col42_Byte1("_Letter_Row00_Col42_Byte1", float) = 0 _Letter_Row00_Col43_Byte1("_Letter_Row00_Col43_Byte1", float) = 0 + _Letter_Row00_Col44_Byte1("_Letter_Row00_Col44_Byte1", float) = 0 + _Letter_Row00_Col45_Byte1("_Letter_Row00_Col45_Byte1", float) = 0 + _Letter_Row00_Col46_Byte1("_Letter_Row00_Col46_Byte1", float) = 0 + _Letter_Row00_Col47_Byte1("_Letter_Row00_Col47_Byte1", float) = 0 _Letter_Row01_Col00_Byte1("_Letter_Row01_Col00_Byte1", float) = 0 _Letter_Row01_Col01_Byte1("_Letter_Row01_Col01_Byte1", float) = 0 _Letter_Row01_Col02_Byte1("_Letter_Row01_Col02_Byte1", float) = 0 @@ -280,6 +301,10 @@ _Letter_Row01_Col41_Byte1("_Letter_Row01_Col41_Byte1", float) = 0 _Letter_Row01_Col42_Byte1("_Letter_Row01_Col42_Byte1", float) = 0 _Letter_Row01_Col43_Byte1("_Letter_Row01_Col43_Byte1", float) = 0 + _Letter_Row01_Col44_Byte1("_Letter_Row01_Col44_Byte1", float) = 0 + _Letter_Row01_Col45_Byte1("_Letter_Row01_Col45_Byte1", float) = 0 + _Letter_Row01_Col46_Byte1("_Letter_Row01_Col46_Byte1", float) = 0 + _Letter_Row01_Col47_Byte1("_Letter_Row01_Col47_Byte1", float) = 0 _Letter_Row02_Col00_Byte1("_Letter_Row02_Col00_Byte1", float) = 0 _Letter_Row02_Col01_Byte1("_Letter_Row02_Col01_Byte1", float) = 0 _Letter_Row02_Col02_Byte1("_Letter_Row02_Col02_Byte1", float) = 0 @@ -324,6 +349,10 @@ _Letter_Row02_Col41_Byte1("_Letter_Row02_Col41_Byte1", float) = 0 _Letter_Row02_Col42_Byte1("_Letter_Row02_Col42_Byte1", float) = 0 _Letter_Row02_Col43_Byte1("_Letter_Row02_Col43_Byte1", float) = 0 + _Letter_Row02_Col44_Byte1("_Letter_Row02_Col44_Byte1", float) = 0 + _Letter_Row02_Col45_Byte1("_Letter_Row02_Col45_Byte1", float) = 0 + _Letter_Row02_Col46_Byte1("_Letter_Row02_Col46_Byte1", float) = 0 + _Letter_Row02_Col47_Byte1("_Letter_Row02_Col47_Byte1", float) = 0 _Letter_Row03_Col00_Byte1("_Letter_Row03_Col00_Byte1", float) = 0 _Letter_Row03_Col01_Byte1("_Letter_Row03_Col01_Byte1", float) = 0 _Letter_Row03_Col02_Byte1("_Letter_Row03_Col02_Byte1", float) = 0 @@ -368,6 +397,10 @@ _Letter_Row03_Col41_Byte1("_Letter_Row03_Col41_Byte1", float) = 0 _Letter_Row03_Col42_Byte1("_Letter_Row03_Col42_Byte1", float) = 0 _Letter_Row03_Col43_Byte1("_Letter_Row03_Col43_Byte1", float) = 0 + _Letter_Row03_Col44_Byte1("_Letter_Row03_Col44_Byte1", float) = 0 + _Letter_Row03_Col45_Byte1("_Letter_Row03_Col45_Byte1", float) = 0 + _Letter_Row03_Col46_Byte1("_Letter_Row03_Col46_Byte1", float) = 0 + _Letter_Row03_Col47_Byte1("_Letter_Row03_Col47_Byte1", float) = 0 } SubShader { @@ -407,6 +440,7 @@ Texture2D _Font_0x8000_0x9FFF; Texture2D _Font_0xA000_0xBFFF; Texture2D _Font_0xC000_0xDFFF; + Texture2D _Img_0xE000_0xE03F; float3 HUEtoRGB(in float H) { @@ -483,6 +517,10 @@ float _Letter_Row00_Col41_Byte0; float _Letter_Row00_Col42_Byte0; float _Letter_Row00_Col43_Byte0; + float _Letter_Row00_Col44_Byte0; + float _Letter_Row00_Col45_Byte0; + float _Letter_Row00_Col46_Byte0; + float _Letter_Row00_Col47_Byte0; float _Letter_Row01_Col00_Byte0; float _Letter_Row01_Col01_Byte0; float _Letter_Row01_Col02_Byte0; @@ -527,6 +565,10 @@ float _Letter_Row01_Col41_Byte0; float _Letter_Row01_Col42_Byte0; float _Letter_Row01_Col43_Byte0; + float _Letter_Row01_Col44_Byte0; + float _Letter_Row01_Col45_Byte0; + float _Letter_Row01_Col46_Byte0; + float _Letter_Row01_Col47_Byte0; float _Letter_Row02_Col00_Byte0; float _Letter_Row02_Col01_Byte0; float _Letter_Row02_Col02_Byte0; @@ -571,6 +613,10 @@ float _Letter_Row02_Col41_Byte0; float _Letter_Row02_Col42_Byte0; float _Letter_Row02_Col43_Byte0; + float _Letter_Row02_Col44_Byte0; + float _Letter_Row02_Col45_Byte0; + float _Letter_Row02_Col46_Byte0; + float _Letter_Row02_Col47_Byte0; float _Letter_Row03_Col00_Byte0; float _Letter_Row03_Col01_Byte0; float _Letter_Row03_Col02_Byte0; @@ -615,6 +661,10 @@ float _Letter_Row03_Col41_Byte0; float _Letter_Row03_Col42_Byte0; float _Letter_Row03_Col43_Byte0; + float _Letter_Row03_Col44_Byte0; + float _Letter_Row03_Col45_Byte0; + float _Letter_Row03_Col46_Byte0; + float _Letter_Row03_Col47_Byte0; float _Letter_Row00_Col00_Byte1; float _Letter_Row00_Col01_Byte1; float _Letter_Row00_Col02_Byte1; @@ -659,6 +709,10 @@ float _Letter_Row00_Col41_Byte1; float _Letter_Row00_Col42_Byte1; float _Letter_Row00_Col43_Byte1; + float _Letter_Row00_Col44_Byte1; + float _Letter_Row00_Col45_Byte1; + float _Letter_Row00_Col46_Byte1; + float _Letter_Row00_Col47_Byte1; float _Letter_Row01_Col00_Byte1; float _Letter_Row01_Col01_Byte1; float _Letter_Row01_Col02_Byte1; @@ -703,6 +757,10 @@ float _Letter_Row01_Col41_Byte1; float _Letter_Row01_Col42_Byte1; float _Letter_Row01_Col43_Byte1; + float _Letter_Row01_Col44_Byte1; + float _Letter_Row01_Col45_Byte1; + float _Letter_Row01_Col46_Byte1; + float _Letter_Row01_Col47_Byte1; float _Letter_Row02_Col00_Byte1; float _Letter_Row02_Col01_Byte1; float _Letter_Row02_Col02_Byte1; @@ -747,6 +805,10 @@ float _Letter_Row02_Col41_Byte1; float _Letter_Row02_Col42_Byte1; float _Letter_Row02_Col43_Byte1; + float _Letter_Row02_Col44_Byte1; + float _Letter_Row02_Col45_Byte1; + float _Letter_Row02_Col46_Byte1; + float _Letter_Row02_Col47_Byte1; float _Letter_Row03_Col00_Byte1; float _Letter_Row03_Col01_Byte1; float _Letter_Row03_Col02_Byte1; @@ -791,6 +853,10 @@ float _Letter_Row03_Col41_Byte1; float _Letter_Row03_Col42_Byte1; float _Letter_Row03_Col43_Byte1; + float _Letter_Row03_Col44_Byte1; + float _Letter_Row03_Col45_Byte1; + float _Letter_Row03_Col46_Byte1; + float _Letter_Row03_Col47_Byte1; v2f vert (appdata v) { @@ -825,7 +891,11 @@ // Write the nth letter in the current cell and return the value of the // pixel. - float2 GetLetter(float2 uv, int nth_letter) + // `texture_rows` and `texture_cols` indicate how many rows and columns are + // in the texture being sampled. + float2 GetLetter(float2 uv, int nth_letter, + float texture_cols, float texture_rows, + float board_cols, float board_rows) { // UV spans from [0,1] to [0,1]. // 'U' is horizontal; cols. @@ -833,13 +903,11 @@ // // I want to divide the mesh into an m x n grid. // I want to know what grid cell I'm in. This is simply u * m, v * n. - int CHAR_ROWS = 4; - int CHAR_COLS = 44; // OK, I know what cell I'm in. Now I need to know how far across it I // am. Produce a float in the range [0, 1). - float CHAR_FRAC_COL = uv.x * CHAR_COLS - floor(uv.x * CHAR_COLS); - float CHAR_FRAC_ROW = uv.y * CHAR_ROWS - floor(uv.y * CHAR_ROWS); + float CHAR_FRAC_COL = uv.x * board_cols - floor(uv.x * board_cols); + float CHAR_FRAC_ROW = uv.y * board_rows - floor(uv.y * board_rows); // Avoid rendering pixels right on the edge of the slot. If we were to // do this, then that value would get stretched due to clamping @@ -851,15 +919,11 @@ return float2(0, 0); } - // This is the number of rows and columns in the actual texture. - float LETTER_COLS = 128.0; - float LETTER_ROWS = 64.0; + float LETTER_COL = fmod(nth_letter, floor(texture_cols)); + float LETTER_ROW = floor(texture_rows) - floor(nth_letter / floor(texture_cols)); - float LETTER_COL = fmod(nth_letter, floor(LETTER_COLS)); - float LETTER_ROW = floor(LETTER_ROWS) - floor(nth_letter / floor(LETTER_COLS)); - - float LETTER_UV_ROW = (LETTER_ROW + CHAR_FRAC_ROW - 1.00) / LETTER_ROWS; - float LETTER_UV_COL = (LETTER_COL + CHAR_FRAC_COL) / LETTER_COLS; + float LETTER_UV_ROW = (LETTER_ROW + CHAR_FRAC_ROW - 1.00) / texture_rows; + float LETTER_UV_COL = (LETTER_COL + CHAR_FRAC_COL) / texture_cols; float2 result; result.x = LETTER_UV_COL; @@ -896,7 +960,7 @@ float2 GetLetterParameter(float2 uv) { float CHAR_ROWS = 4.0; - float CHAR_COLS = 44.0; + float CHAR_COLS = 48.0; float CHAR_COL = floor(uv.x * CHAR_COLS); float CHAR_ROW = floor(uv.y * CHAR_ROWS); @@ -992,6 +1056,14 @@ return float2(_Letter_Row00_Col42_Byte0, _Letter_Row00_Col42_Byte1); case 43: return float2(_Letter_Row00_Col43_Byte0, _Letter_Row00_Col43_Byte1); + case 44: + return float2(_Letter_Row00_Col44_Byte0, _Letter_Row00_Col44_Byte1); + case 45: + return float2(_Letter_Row00_Col45_Byte0, _Letter_Row00_Col45_Byte1); + case 46: + return float2(_Letter_Row00_Col46_Byte0, _Letter_Row00_Col46_Byte1); + case 47: + return float2(_Letter_Row00_Col47_Byte0, _Letter_Row00_Col47_Byte1); default: return float2(0, 0); } @@ -1085,6 +1157,14 @@ return float2(_Letter_Row01_Col42_Byte0, _Letter_Row01_Col42_Byte1); case 43: return float2(_Letter_Row01_Col43_Byte0, _Letter_Row01_Col43_Byte1); + case 44: + return float2(_Letter_Row01_Col44_Byte0, _Letter_Row01_Col44_Byte1); + case 45: + return float2(_Letter_Row01_Col45_Byte0, _Letter_Row01_Col45_Byte1); + case 46: + return float2(_Letter_Row01_Col46_Byte0, _Letter_Row01_Col46_Byte1); + case 47: + return float2(_Letter_Row01_Col47_Byte0, _Letter_Row01_Col47_Byte1); default: return float2(0, 0); } @@ -1178,6 +1258,14 @@ return float2(_Letter_Row02_Col42_Byte0, _Letter_Row02_Col42_Byte1); case 43: return float2(_Letter_Row02_Col43_Byte0, _Letter_Row02_Col43_Byte1); + case 44: + return float2(_Letter_Row02_Col44_Byte0, _Letter_Row02_Col44_Byte1); + case 45: + return float2(_Letter_Row02_Col45_Byte0, _Letter_Row02_Col45_Byte1); + case 46: + return float2(_Letter_Row02_Col46_Byte0, _Letter_Row02_Col46_Byte1); + case 47: + return float2(_Letter_Row02_Col47_Byte0, _Letter_Row02_Col47_Byte1); default: return float2(0, 0); } @@ -1271,6 +1359,14 @@ return float2(_Letter_Row03_Col42_Byte0, _Letter_Row03_Col42_Byte1); case 43: return float2(_Letter_Row03_Col43_Byte0, _Letter_Row03_Col43_Byte1); + case 44: + return float2(_Letter_Row03_Col44_Byte0, _Letter_Row03_Col44_Byte1); + case 45: + return float2(_Letter_Row03_Col45_Byte0, _Letter_Row03_Col45_Byte1); + case 46: + return float2(_Letter_Row03_Col46_Byte0, _Letter_Row03_Col46_Byte1); + case 47: + return float2(_Letter_Row03_Col47_Byte0, _Letter_Row03_Col47_Byte1); default: return float2(0, 0); } @@ -1325,8 +1421,18 @@ int2 letter_bytes = (int2) floor(GetLetterParameter(uv)); int letter = letter_bytes[0] | (letter_bytes[1] << 8); - - uv = GetLetter(uv, letter); + + float texture_cols; + float texture_rows; + if (letter < 0xE000) { + texture_cols = 128.0; + texture_rows = 64.0; + uv = GetLetter(uv, letter, texture_cols, texture_rows, 48, 4); + } else { + texture_cols = 8.0; + texture_rows = 8.0; + uv = GetLetter(uv, letter, texture_cols, texture_rows, 8, 4); + } fixed4 background = TaSTT_Backplate.Sample(sampler_linear_repeat, uv); fixed4 text; @@ -1356,7 +1462,7 @@ text = _Font_0xC000_0xDFFF.Sample(sampler_linear_repeat, uv); break; default: - text = _Font_0x0000_0x1FFF.Sample(sampler_linear_repeat, uv); + text = _Img_0xE000_0xE03F.Sample(sampler_linear_repeat, uv); break; } fixed4 black = fixed4(0,0,0,0); diff --git a/emotes.py b/emotes.py new file mode 100644 index 0000000..0fd41d8 --- /dev/null +++ b/emotes.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +import argparse +from math import floor +import os +# python3 -m pip install pillow +from PIL import Image +import sys + +# (row, col) +TEX_SZ = (2048, 2048) + +IMG_SZ_PX = 256 +IMG_PER_ROW = int(TEX_SZ[0] / IMG_SZ_PX) +IMG_PER_COL = int(TEX_SZ[1] / IMG_SZ_PX) + +# TODO(yum) this should live in a config file. +# Note: the name of the emote must be no longer than 6 characters. +IMG_TEX_DATA = [] +IMG_TEX_DATA.append(("Images/Emotes/xdd.png", "xdd")) +IMG_TEX_DATA.append(("Images/Emotes/pog.png", "pog")) +IMG_TEX_DATA.append(("Images/Emotes/lulw.png", "laugh")) +IMG_TEX_DATA.append(("Images/Emotes/bighardo.png", "hard")) +IMG_TEX_DATA.append(("Images/Emotes/peepoHappy.png", "happy")) +IMG_TEX_DATA.append(("Images/Emotes/peepoSad.png", "sad")) +IMG_TEX_DATA.append(("Images/Emotes/bedge.png", "bed")) +IMG_TEX_DATA.append(("Images/Emotes/reallymad.png", "mad")) + +IMG_TEX_KEYWORD_TO_COORD = {} +for i in range(0, len(IMG_TEX_DATA)): + IMG_TEX_KEYWORD_TO_COORD[IMG_TEX_DATA[i][1]] = i + +# We treat images like words. To keep things simple, they're the same height as +# a word, and they're a fixed width. +IMG_SZ_LETTER_ROWS = 1 +IMG_SZ_LETTER_COLS = 6 + +def lookup(word: str): + word = word.lower() + word = ''.join(c for c in word.lower() if c.isalpha()) + if word in IMG_TEX_KEYWORD_TO_COORD.keys(): + return word, IMG_TEX_KEYWORD_TO_COORD[word] + return None, None + +def openTexture(tex_path: str): + if not os.path.exists(args.texture_path): + return Image.new("RGB", TEX_SZ) + tex = Image.open(args.texture_path) + if tex.size != TEX_SZ: + print("Texture at {} has mismatching size {}, creating new texture".format( + tex_path, tex.size), file=sys.stderr) + return Image.new("RGB", TEX_SZ) + return tex + +# Add an image to the texture at the coordinates (x, y). x and y should be in +# the range [0, IMG_PER_COL) and [0, IMG_PER_ROW) respectively. +def addImageToTexture(tex: Image, img_path: str, x: int, y:int): + # Transparent images will be composited on top of a black background. + img = Image.open(img_path).convert('RGBA') + img_bg = Image.new('RGBA', img.size, (0, 0, 0)) + img = Image.alpha_composite(img_bg, img).convert('RGB') + + max_px = IMG_SZ_PX + + # Scale the image up so it uses as much space as is given to it. + # I originally planned to support multiple scales, but this proved to be + # too much work - getting line wrapping to work with this would be a pain. + # So for now, all images are the same height as words. + scale = 1 + img_x, img_y = img.size + max_dim = max(img_x, img_y) + img_scale = (max_px / max_dim) * scale + new_sz = (int(floor(img.size[0] * img_scale)), + int(floor(img.size[1] * img_scale))) + print("Original size: {}".format(img.size)) + print("Scaled size: {}".format(new_sz)) + img = img.resize(new_sz) + + # Center the image within its new coordinate space. + padded_img_sz = (IMG_SZ_PX * scale, IMG_SZ_PX * scale) + padded_img = Image.new("RGB", padded_img_sz) + centered_x = int(floor((padded_img_sz[0] - new_sz[0]) / 2)) + centered_y = int(floor((padded_img_sz[1] - new_sz[1]) / 2)) + padded_img.paste(img, box=(centered_x, centered_y)) + img = padded_img + + # Break the image into tiles and write them into the texture. + for slot in range(0, scale * scale): + tile_x = slot % scale + tile_y = int(floor(slot / scale)) + tile_bbox = (tile_x * IMG_SZ_PX, tile_y * IMG_SZ_PX, (tile_x + 1) * IMG_SZ_PX, (tile_y + 1) * IMG_SZ_PX) + tile = img.crop(tile_bbox) + print("tile {},{} (bbox={})".format(tile_x, tile_y, tile_bbox)) + + slot_x = x + slot % IMG_PER_ROW + slot_y = y + int(floor(slot / IMG_PER_ROW)) + slot_x_px = slot_x * IMG_SZ_PX + slot_y_px = slot_y * IMG_SZ_PX + print("Add img at {},{} (px {},{})".format(slot_x, slot_y, slot_x_px, slot_y_px)) + + tex.paste(tile, box=(slot_x_px, slot_y_px)) + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("--texture_path", type=str, help="Path to save the generated texture.") + args = parser.parse_args() + + if not args.texture_path: + args.texture_path = "img_texture.png" + + return args + +if __name__ == "__main__": + args = parseArgs() + + tex = openTexture(args.texture_path) + for i in range(0, len(IMG_TEX_DATA)): + filename = IMG_TEX_DATA[i][0] + x = i % IMG_PER_ROW + y = int(floor(i / IMG_PER_ROW)) + addImageToTexture(tex, filename, x, y) + tex.save(args.texture_path) + diff --git a/generate_utils.py b/generate_utils.py index e8c60b8..0480c3c 100644 --- a/generate_utils.py +++ b/generate_utils.py @@ -10,18 +10,12 @@ def replaceMacros(lines, macro_defs): # the last cell will (with the current implementation) wrap around to the front # of the board. BOARD_ROWS=4 -BOARD_COLS=44 -INDEX_BITS=4 +BOARD_COLS=48 +NUM_REGIONS = 16 CHARS_PER_CELL=256 BYTES_PER_CHAR=2 -NUM_LAYERS=ceil((BOARD_ROWS * BOARD_COLS) / (2**INDEX_BITS)) - -# The bits per layer are: -# 8 bits: letter selection (256 possible letters per slot) -# 3 bits: slot selection (each layer controls 8 slots) -# 1 bit: enable bit (turns layer off while we index to a new slot) -NUM_PARAM_BITS=(NUM_LAYERS * (8 + INDEX_BITS + 1)) +NUM_LAYERS=ceil((BOARD_ROWS * BOARD_COLS) / NUM_REGIONS) # Implementation detail. We use this parameter to return from the terminal # state of the FX layer to the starting state. diff --git a/libtastt.py b/libtastt.py index 36ccec7..bee535f 100644 --- a/libtastt.py +++ b/libtastt.py @@ -373,7 +373,7 @@ def generateFXLayer(which_layer: int, anim: libunity.UnityAnimator, layer: enable_param, True) select_states = {} - for i in range(0, 2 ** generate_utils.INDEX_BITS): + for i in range(0, generate_utils.NUM_REGIONS): dx = i * 200 dy = 200 diff --git a/osc_ctrl.py b/osc_ctrl.py index 46c7997..34d1a36 100644 --- a/osc_ctrl.py +++ b/osc_ctrl.py @@ -20,6 +20,8 @@ from generate_utils import NUM_LAYERS from generate_utils import BOARD_ROWS from generate_utils import BOARD_COLS +import emotes + # Based on a couple experiments, this seems like about as fast as we can go # before players start losing events. SYNC_FREQ_HZ = 5.0 @@ -53,12 +55,33 @@ def encodeMessage(lines): result = [] lines_tmp = lines + [" "] * ((BOARD_ROWS - len(lines)) % BOARD_ROWS) for line in lines_tmp: - #print("encode line {}".format(line)) - for char in line: - if not char in state.encoding: - print("skip unrecognized char {}".format(char)) + first_word = True + for word in line.split(): + if first_word: + first_word = False + else: + result.append(state.encoding[' ']) + + emote_word, emote_word_idx = emotes.lookup(word) + if emote_word: + + word_align = 0 + if len(result) > 0: + word_align = (6 - len(result) % 6) % 6 + word = ' ' * word_align + word + + for i in range(0, word_align): + result.append(state.encoding[' ']) + + for i in range(0, 6): + result.append((emote_word_idx, 0xE0)) continue - result.append(state.encoding[char]) + + for char in word: + if not char in state.encoding: + print("skip unrecognized char {}".format(char)) + continue + result.append(state.encoding[char]) result += [state.encoding[' ']] * (BOARD_COLS - len(line)) return result @@ -77,7 +100,7 @@ def disable(client): client.send_message(addr, False) # Send a cell all at once. -# `which_cell` is an integer in the range [0,2**INDEX_BITS). +# `which_cell` is an integer in the range [0,NUM_REGIONS) def sendMessageCellDiscrete(client, msg_cell, which_cell): empty_cell = [state.encoding[' ']] * NUM_LAYERS @@ -86,7 +109,7 @@ def sendMessageCellDiscrete(client, msg_cell, which_cell): client.send_message(addr, True) # Really long messages just wrap back around. - which_cell = (which_cell % (2 ** generate_utils.INDEX_BITS)) + which_cell = (which_cell % generate_utils.NUM_REGIONS) enable(client) @@ -113,10 +136,26 @@ def splitMessage(msg): lines = [] line = "" for word in msg.split(): + # Hack: if the word is an emote, we make it 6 characters wide, then + # encode it differently later on. + emote_word, emote_word_idx = emotes.lookup(word) + if emote_word: + word = word.ljust(6) + # Due to some fuckery I do in the shader, emotes have to be rendered on + # a 6-character boundary. So pad the word on the left with spaces + # to get it onto that boundary. + word_align = 0 + if len(line) > 0: + word_align = (6 - (len(line) + 1) % 6) % 6 + print("len line: {}".format(len(line))) + print("word align: {}".format(word_align)) + word = ' ' * word_align + word + while len(word) > BOARD_COLS: if len(line) != 0: lines.append(line) line = "" + word_prefix = word[0:BOARD_COLS-1] + "-" word_suffix = word[BOARD_COLS-1:] #print("append prefix {}".format(word_prefix)) @@ -308,11 +347,6 @@ if __name__ == "__main__": state.encoding = generateEncoding() - sendRawMessage(client, [ \ - (65,0), \ - (0x20,0xAD), \ - ]) - tx_state = OscTxState() for line in fileinput.input(): while sendMessageLazy(client, line, tx_state) != SEND_MSG_LAZY_DONE: -- cgit v1.2.3