From 9d2829b2ead4c569b5f47c41f185a052383ea9c0 Mon Sep 17 00:00:00 2001 From: DJLevel3 Date: Wed, 22 Jan 2025 08:37:45 -0500 Subject: [PATCH] Convert live object rendering to binary (leaving legacy support in) --- Source/gpla/LineArtParser.cpp | 94 +++++++++++++++++++++------------ Source/gpla/LineArtParser.h | 10 ++-- Source/obj/ObjectServer.cpp | 74 ++++++++++++++++---------- blender/osci_render/__init__.py | 86 ++++++++++++------------------ 4 files changed, 147 insertions(+), 117 deletions(-) diff --git a/Source/gpla/LineArtParser.cpp b/Source/gpla/LineArtParser.cpp index e191ed5..73c3223 100644 --- a/Source/gpla/LineArtParser.cpp +++ b/Source/gpla/LineArtParser.cpp @@ -2,14 +2,18 @@ LineArtParser::LineArtParser(juce::String json) { - parseJsonFrames(json); + frames.clear(); + numFrames = 0; + frames = parseJsonFrames(json); + numFrames = frames.size(); } LineArtParser::LineArtParser(char* data, int dataLength) { - parseBinaryFrames(data, dataLength); - if (numFrames == 0) { - parseJsonFrames(juce::String(BinaryData::fallback_gpla, BinaryData::fallback_gplaSize)); - } + frames.clear(); + numFrames = 0; + frames = parseBinaryFrames(data, dataLength); + numFrames = frames.size(); + if (numFrames == 0) frames = epicFail(); } LineArtParser::~LineArtParser() { @@ -26,11 +30,16 @@ void LineArtParser::makeChars(int64_t data, char* chars) { } } -void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { - frames.clear(); - numFrames = 0; +std::vector> LineArtParser::epicFail() { + return parseJsonFrames(juce::String(BinaryData::fallback_gpla, BinaryData::fallback_gplaSize)); +} + +std::vector> LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { int64_t* data = (int64_t*)bytes; - int dataLength = bytesLength / 4; + int dataLength = bytesLength / 8; + std::vector> tFrames; + + if (dataLength < 4) return epicFail(); int index = 0; int64_t rawData = data[index]; @@ -39,31 +48,37 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { char tag[9] = " "; makeChars(rawData, tag); - if (strcmp(tag, "GPLA ") != 0) return; + if (strcmp(tag, "GPLA ") != 0) return epicFail(); // Major + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; // Minor + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; // Patch + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); - if (strcmp(tag, "FILE ") != 0) return; + if (strcmp(tag, "FILE ") != 0) return epicFail(); int reportedNumFrames = 0; int frameRate = 0; + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); while (strcmp(tag, "DONE ") != 0) { + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; @@ -73,24 +88,29 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { frameRate = rawData; } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); while (strcmp(tag, "END GPLA") != 0) { if (strcmp(tag, "FRAME ") == 0) { - std::vector> allMatrices; - std::vector>> allVertices; - double focalLength; + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); + + double focalLength; + std::vector> allMatrices; + std::vector>> allVertices; while (strcmp(tag, "OBJECTS ") != 0) { + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; @@ -98,11 +118,13 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { focalLength = makeDouble(rawData); } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); @@ -111,6 +133,7 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { if (strcmp(tag, "OBJECT ") == 0) { std::vector> vertices; std::vector matrix; + if (index >= dataLength) return epicFail(); int strokeNum = 0; rawData = data[index]; index++; @@ -119,13 +142,16 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { if (strcmp(tag, "MATRIX ") == 0) { matrix.clear(); for (int i = 0; i < 16; i++) { + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; matrix.push_back(makeDouble(rawData)); } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; } else if (strcmp(tag, "STROKES ") == 0) { + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); @@ -133,6 +159,7 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { while (strcmp(tag, "DONE ") != 0) { if (strcmp(tag, "STROKE ") == 0) { vertices.push_back(std::vector()); + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); @@ -140,6 +167,7 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { int vertexCount = 0; while (strcmp(tag, "DONE ") != 0) { if (strcmp(tag, "vertexCt") == 0) { + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; vertexCount = rawData; @@ -149,6 +177,7 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { double y = 0; double z = 0; for (int i = 0; i < vertexCount; i++) { + if (index + 2 >= dataLength) return epicFail(); rawData = data[index]; index++; x = makeDouble(rawData); @@ -163,26 +192,31 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { vertices[strokeNum].push_back(OsciPoint(x, y, z)); } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); while (strcmp(tag, "DONE ") != 0) { + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } strokeNum++; } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); @@ -192,25 +226,24 @@ void LineArtParser::parseBinaryFrames(char* bytes, int bytesLength) { vertices.clear(); matrix.clear(); } + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } - - frames.push_back(assembleFrame(allVertices, allMatrices, focalLength)); + std::vector frame = assembleFrame(allVertices, allMatrices, focalLength); + tFrames.push_back(frame); } - + if (index >= dataLength) return epicFail(); rawData = data[index]; index++; makeChars(rawData, tag); } - numFrames = frames.size(); - return; + return tFrames; } -void LineArtParser::parseJsonFrames(juce::String jsonStr) { - frames.clear(); - numFrames = 0; +std::vector> LineArtParser::parseJsonFrames(juce::String jsonStr) { + std::vector> frames; // format of json is: // { @@ -243,19 +276,13 @@ void LineArtParser::parseJsonFrames(juce::String jsonStr) { auto json = juce::JSON::parse(jsonStr); // If json parse failed, stop and parse default fallback instead - if (json.isVoid()) { - parseJsonFrames(juce::String(BinaryData::fallback_gpla, BinaryData::fallback_gplaSize)); - return; - } + if (json.isVoid()) return epicFail(); auto jsonFrames = *json.getProperty("frames", juce::Array()).getArray(); - numFrames = jsonFrames.size(); + int numFrames = jsonFrames.size(); // If json does not contain any frames, stop and parse no-frames fallback instead - if (numFrames == 0) { - parseJsonFrames(juce::String(BinaryData::noframes_gpla, BinaryData::noframes_gplaSize)); - return; - } + if (numFrames == 0) return parseJsonFrames(juce::String(BinaryData::noframes_gpla, BinaryData::noframes_gplaSize)); bool hasValidFrames = false; @@ -275,10 +302,7 @@ void LineArtParser::parseJsonFrames(juce::String jsonStr) { } // If no frames were valid, stop and parse invalid fallback instead - if (!hasValidFrames) { - parseJsonFrames(juce::String(BinaryData::invalid_gpla, BinaryData::invalid_gplaSize)); - return; - } + if (!hasValidFrames) return parseJsonFrames(juce::String(BinaryData::invalid_gpla, BinaryData::invalid_gplaSize)); } void LineArtParser::setFrame(int fNum) { diff --git a/Source/gpla/LineArtParser.h b/Source/gpla/LineArtParser.h index 5c387db..99da662 100644 --- a/Source/gpla/LineArtParser.h +++ b/Source/gpla/LineArtParser.h @@ -14,12 +14,14 @@ public: void setFrame(int fNum); std::vector> draw(); + static std::vector> parseJsonFrames(juce::String jsonStr); + static std::vector> parseBinaryFrames(char* data, int dataLength); + static std::vector generateFrame(juce::Array < juce::var> objects, double focalLength); private: - void parseJsonFrames(juce::String jsonStr); - void parseBinaryFrames(char* data, int dataLength); - double makeDouble(int64_t data); - void makeChars(int64_t data, char* chars); + static std::vector> epicFail(); + static double makeDouble(int64_t data); + static void makeChars(int64_t data, char* chars); static std::vector> reorderVertices(std::vector> vertices); static std::vector assembleFrame(std::vector>> allVertices, std::vector> allMatrices, double focalLength); int frameNumber = 0; diff --git a/Source/obj/ObjectServer.cpp b/Source/obj/ObjectServer.cpp index 788da19..50c0cf5 100644 --- a/Source/obj/ObjectServer.cpp +++ b/Source/obj/ObjectServer.cpp @@ -25,6 +25,7 @@ void ObjectServer::run() { while (!threadShouldExit() && connection->isConnected()) { if (connection->waitUntilReady(true, 200) == 1) { int i = 0; + std::vector frameContainer; // read until we get a newline while (!threadShouldExit()) { @@ -52,36 +53,55 @@ void ObjectServer::run() { break; } - // format of json is: - // { - // "objects": [ - // { - // "name": "Line Art", - // "vertices": [ - // [ - // { - // "x": double value, - // "y": double value, - // "z": double value - // }, - // ... - // ], - // ... - // ], - // "matrix": [ - // 16 double values - // ] - // } - // ], - // "focalLength": double value - // } + if (strncmp(message.get(), "R1BMQSAg", 8) == 0) { + juce::MemoryOutputStream binStream; + juce::String messageString = message.get(); + if (juce::Base64::convertFromBase64(binStream, messageString)) { + std::vector< std::vector> receivedFrames; + int bytesRead = binStream.getDataSize(); + if (bytesRead < 8) return; + char* gplaData = (char*)binStream.getData(); + receivedFrames = LineArtParser::parseBinaryFrames(gplaData, bytesRead); + if (receivedFrames.size() <= 0) continue; + frameContainer = receivedFrames[0]; + } + else { + continue; + } + } + else { - auto json = juce::JSON::parse(message.get()); + // format of json is: + // { + // "objects": [ + // { + // "name": "Line Art", + // "vertices": [ + // [ + // { + // "x": double value, + // "y": double value, + // "z": double value + // }, + // ... + // ], + // ... + // ], + // "matrix": [ + // 16 double values + // ] + // } + // ], + // "focalLength": double value + // } - juce::Array objects = *json.getProperty("objects", juce::Array()).getArray(); - double focalLength = json.getProperty("focalLength", 1); + auto json = juce::JSON::parse(message.get()); - std::vector frameContainer = LineArtParser::generateFrame(objects, focalLength); + juce::Array objects = *json.getProperty("objects", juce::Array()).getArray(); + double focalLength = json.getProperty("focalLength", 1); + + frameContainer = LineArtParser::generateFrame(objects, focalLength); + } std::vector> frame; diff --git a/blender/osci_render/__init__.py b/blender/osci_render/__init__.py index 6ddd39d..0453a4e 100644 --- a/blender/osci_render/__init__.py +++ b/blender/osci_render/__init__.py @@ -17,6 +17,7 @@ import socket import json import atexit import struct +import base64 from bpy.props import StringProperty from bpy.app.handlers import persistent from bpy_extras.io_utils import ImportHelper @@ -125,53 +126,7 @@ def close_osci_render(): except socket.error as exp: sock = None - -def append_matrix(object_info, obj): - camera_space = bpy.context.scene.camera.matrix_world.inverted() @ obj.matrix_world - object_info["matrix"] = [camera_space[i][j] for i in range(4) for j in range(4)] - return object_info - -def get_frame_info(): - frame_info = {"objects": []} - if (bpy.app.version[0] > 4) or (bpy.app.version[0] == 4 and bpy.app.version[1] >= 3): - for object in bpy.data.objects: - if object.visible_get() and object.type == 'GREASEPENCIL': - dg = bpy.context.evaluated_depsgraph_get() - obj = object.evaluated_get(dg) - object_info = {"name": object.name} - for layer in obj.data.layers: - strokes = layer.frames.data.current_frame().drawing.strokes - object_info["vertices"] = [] - for stroke in strokes: - object_info["vertices"].append([{ - "x": vert.position.x, - "y": vert.position.y, - "z": vert.position.z, - } for vert in stroke.points]) - frame_info["objects"].append(append_matrix(object_info, object)) - else: - for object in bpy.data.objects: - if object.visible_get() and object.type == 'GPENCIL': - dg = bpy.context.evaluated_depsgraph_get() - obj = object.evaluated_get(dg) - object_info = {"name": object.name} - strokes = obj.data.layers.active.frames.data.active_frame.strokes - object_info["vertices"] = [] - for stroke in strokes: - object_info["vertices"].append([{ - "x": vert.co[0], - "y": vert.co[1], - "z": vert.co[2], - } for vert in stroke.points]) - frame_info["objects"].append(append_matrix(object_info, obj)) - - frame_info["focalLength"] = -0.05 * bpy.data.cameras[0].lens - - return frame_info - -@persistent -def save_scene_to_file(scene, file_path): - return_frame = scene.frame_current +def get_gpla_file_allframes(scene): bin = bytearray() # header @@ -194,6 +149,37 @@ def save_scene_to_file(scene, file_path): bin.extend(("END GPLA").encode("utf8")) + return bin + +def get_gpla_file(scene): + bin = bytearray() + + # header + bin.extend(("GPLA ").encode("utf8")) + bin.extend(GPLA_MAJOR.to_bytes(8, "little")) + bin.extend(GPLA_MINOR.to_bytes(8, "little")) + bin.extend(GPLA_PATCH.to_bytes(8, "little")) + + # file info + bin.extend(("FILE ").encode("utf8")) + bin.extend(("fCount ").encode("utf8")) + bin.extend((scene.frame_end - scene.frame_start + 1).to_bytes(8, "little")) + bin.extend(("fRate ").encode("utf8")) + bin.extend(scene.render.fps.to_bytes(8, "little")) + bin.extend(("DONE ").encode("utf8")) + + bin.extend(get_frame_info_binary()) + + bin.extend(("END GPLA").encode("utf8")) + + return bin + +@persistent +def save_scene_to_file(scene, file_path): + return_frame = scene.frame_current + + bin = get_gpla_file_allframes(scene) + if file_path is not None: with open(file_path, "wb") as f: f.write(bytes(bin)) @@ -308,11 +294,9 @@ def send_scene_to_osci_render(scene): global sock if sock is not None: - frame_info = get_frame_info() - - json_str = json.dumps(frame_info, separators=(',', ':')) + '\n' + bin = get_gpla_file(scene) try: - sock.sendall(json_str.encode('utf-8')) + sock.sendall(base64.b64encode(bytes(bin)) + "\n".encode("utf8")) except socket.error as exp: sock = None