diff --git a/NeutrinoJSONTest.ipynb b/NeutrinoJSONTest.ipynb deleted file mode 100644 index 115d213..0000000 --- a/NeutrinoJSONTest.ipynb +++ /dev/null @@ -1,245 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 217, - "source": [ - "import json\r\n", - "\r\n", - "# create a new scene graph\r\n", - "def new_scene(name):\r\n", - " # create empty neutrino data\r\n", - " data = {\r\n", - " \"meta\": {\r\n", - " \"name\": (\"name\", name),\r\n", - " \"scale\": (\"float\", 1.0),\r\n", - " \"asset_path\": (\"path\", \"./\"),\r\n", - " },\r\n", - " \"graph\": {\r\n", - " \"scene\": {},\r\n", - " \"assets\": {}\r\n", - " },\r\n", - " \"internal\": {\r\n", - " \"max_object_key\": {\"index\": 0},\r\n", - " \"max_cache_key\": {\"index\": 0}\r\n", - " }\r\n", - " }\r\n", - "\r\n", - " # return that empty data\r\n", - " return data\r\n", - "\r\n", - "# write the data to a JSON file\r\n", - "def save_scene(data, readable):\r\n", - " # create working copy of the scene data\r\n", - " clean_data = data.copy()\r\n", - "\r\n", - " # get rid of internal data (not to be exported)\r\n", - " del clean_data[\"internal\"]\r\n", - " \r\n", - " filename = data[\"meta\"][\"name\"][1].replace(\" \", \"\") + \".json\"\r\n", - " with open(filename, \"w\") as outfile:\r\n", - " if readable:\r\n", - " json.dump(clean_data, outfile, indent = 4)\r\n", - " else:\r\n", - " json.dump(clean_data, outfile)\r\n", - "\r\n", - "# get a new indexed object key and track it\r\n", - "def new_key(index):\r\n", - " # get the indexed key\r\n", - " key = hex(index[\"index\"] + 1)\r\n", - "\r\n", - " # index the max key\r\n", - " index[\"index\"] += 1\r\n", - "\r\n", - " return key\r\n", - "\r\n", - "# add an asset to the graph\r\n", - "def add_asset(data, name, path):\r\n", - " asset_data = {\r\n", - " \"name\": (\"name\", name),\r\n", - " \"file\": (\"path\", path)\r\n", - " }\r\n", - " \r\n", - " # add the asset to the graph\r\n", - " data[\"graph\"][\"assets\"][new_key(data[\"internal\"][\"max_object_key\"])] = (\"asset\", asset_data)\r\n", - "\r\n", - "# add an object to the scene\r\n", - "def spawn_object(data, name, asset):\r\n", - " object_data = {\r\n", - " \"name\": (\"name\", name),\r\n", - " \"asset\": \"\",\r\n", - " \"trans\": (\"trans\", {\r\n", - " \"position\": (\"vec3\", [0.0, 0.0, 0.0]),\r\n", - " \"rotation\": (\"vec3\", [0.0, 0.0, 0.0]),\r\n", - " \"scale\": (\"vec3\", [1.0, 1.0, 1.0])\r\n", - " })\r\n", - " }\r\n", - "\r\n", - " # get an asset key by the provided name\r\n", - " for key, value in data[\"graph\"][\"assets\"].items():\r\n", - " if value[1][\"name\"][1] == asset:\r\n", - " object_data[\"asset\"] = f\"*{key}\"\r\n", - "\r\n", - " # add the object to the scene\r\n", - " data[\"graph\"][\"scene\"][new_key(data[\"internal\"][\"max_object_key\"])] = (\"object\", object_data)" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "### Implement SPORC for storage/memory optimization\r\n", - "(Single-Pointer Objective Relational Cache)" - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 218, - "source": [ - "# recursively cache a single typeval tuple object\r\n", - "def cache_typeval(cache, typeval):\r\n", - " # ignore if not typeval\r\n", - " if type(typeval) == tuple:\r\n", - " for key, value in typeval[1].items():\r\n", - " # refuse to cache pointers (that's just... that would just be a nightmare)\r\n", - " if type(value) == str:\r\n", - " is_pointer = (\"*\" in value)\r\n", - " else:\r\n", - " is_pointer = False\r\n", - " if not is_pointer:\r\n", - " # cache member objects if it's a dictionary object\r\n", - " if type(value[1]) == dict:\r\n", - " cache_typeval(cache, value)\r\n", - "\r\n", - " value_hash = hash(str(value))\r\n", - "\r\n", - " # track in cache\r\n", - " if value_hash not in cache[\"objects\"]:\r\n", - " cache_pointer = new_key(cache[\"key_index\"])\r\n", - " cache[\"objects\"][value_hash] = {\"key\": cache_pointer, \"value\": value, \"count\": 1}\r\n", - " else:\r\n", - " cache_pointer = cache[\"objects\"][value_hash][\"key\"]\r\n", - " cache[\"objects\"][value_hash][\"count\"] += 1\r\n", - "\r\n", - " # replace real value with hash\r\n", - " typeval[1][key] = \"#\" + cache_pointer\r\n", - "\r\n", - "# if there's only one instance of a certain value, convert it back to the original value and destroy the cached version\r\n", - "def uncache_typeval(cache, typeval):\r\n", - " for key, value in typeval[1].items():\r\n", - " # refuse to cache pointers (that's just... that would just be a nightmare)\r\n", - " if type(value) == str:\r\n", - " is_pointer = (\"*\" in value)\r\n", - " else:\r\n", - " is_pointer = False\r\n", - " if not is_pointer:\r\n", - " # cache member objects if it's a dictionary object\r\n", - " if type(value[1]) == dict:\r\n", - " uncache_typeval(cache, value)\r\n", - "\r\n", - " value_hash = hash(str(value))\r\n", - "\r\n", - " # check if it occurs only once\r\n", - " cache_key = value.replace(\"#\", \"\")\r\n", - " if cache[cache_key][\"count\"] <= 1:\r\n", - " # replace the cache pointer in the scene data with its original value\r\n", - " typeval[1][key] = cache[cache_key][\"value\"]\r\n", - "\r\n", - " # delete this object from the cache\r\n", - " del cache[cache_key]\r\n", - "\r\n", - "# cache the scene\r\n", - "def cache_scene(data):\r\n", - " # add the cache object to the scene data\r\n", - " data[\"cache\"] = {}\r\n", - "\r\n", - " containers = [\r\n", - " data[\"graph\"][\"scene\"],\r\n", - " data[\"graph\"][\"assets\"]\r\n", - " ]\r\n", - "\r\n", - " # build a cache of value hashes and pointers\r\n", - " hash_cache = {\"key_index\": {\"index\": 0}, \"objects\": {}}\r\n", - " for objects in containers:\r\n", - " for key, value in objects.items():\r\n", - " cache_typeval(hash_cache, value)\r\n", - "\r\n", - " # create a cache hashed with pointer keys instead of value hashes\r\n", - " key_cache = {}\r\n", - " for key, value in hash_cache[\"objects\"].items():\r\n", - " key_cache[value[\"key\"]] = {\"value\": value[\"value\"], \"count\": value[\"count\"]}\r\n", - "\r\n", - " # prune the cache to only redirect repeat values\r\n", - " for objects in containers:\r\n", - " for key, value in objects.items():\r\n", - " uncache_typeval(key_cache, value)\r\n", - "\r\n", - " # create a serialized cache usable by neutrino\r\n", - " serial_cache = {}\r\n", - " for key, value in key_cache.items():\r\n", - " serial_cache[key] = value[\"value\"]\r\n", - "\r\n", - " # add that cache to the neutrino scene data\r\n", - " data[\"cache\"] = serial_cache" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 219, - "source": [ - "# just returns a random string\r\n", - "import random\r\n", - "import string\r\n", - "def random_string(length):\r\n", - " return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))\r\n", - "\r\n", - "# create test scene\r\n", - "test_scene = new_scene(\"Neutrino Test Scene\")\r\n", - "\r\n", - "# populate assets\r\n", - "asset_names = []\r\n", - "for i in range(10):\r\n", - " name = random_string(8)\r\n", - " add_asset(test_scene, name, \"Assets/TestAsset.obj\")\r\n", - " asset_names.append(name)\r\n", - "\r\n", - "# populate objects in scene\r\n", - "for i in range(50):\r\n", - " spawn_object(test_scene, random_string(8), random.choice(asset_names))\r\n", - "\r\n", - "cache_scene(test_scene)\r\n", - "save_scene(test_scene, False)" - ], - "outputs": [], - "metadata": {} - } - ], - "metadata": { - "orig_nbformat": 4, - "language_info": { - "name": "python", - "version": "3.7.8", - "mimetype": "text/x-python", - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "pygments_lexer": "ipython3", - "nbconvert_exporter": "python", - "file_extension": ".py" - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3.7.8 64-bit" - }, - "interpreter": { - "hash": "57baa5815c940fdaff4d14510622de9616cae602444507ba5d0b6727c008cbd6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/NeutrinoParseTesting.ipynb b/NeutrinoParseTesting.ipynb deleted file mode 100644 index 0c14b65..0000000 --- a/NeutrinoParseTesting.ipynb +++ /dev/null @@ -1,135 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 86, - "source": [ - "file = open(\"testB.neu\")\r\n", - "dirty_blob = file.read()\r\n", - "\r\n", - "# get rid of comments and leading/trailing whitespace\r\n", - "lines = dirty_blob.split(\"\\n\")\r\n", - "for i, line in enumerate(lines):\r\n", - " if line.strip()[:2] == \"//\":\r\n", - " del lines[i]\r\n", - "\r\n", - "for i, line in enumerate(lines):\r\n", - " lines[i] = line.strip()\r\n", - "\r\n", - "tidy_blob = \" \".join(lines)" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 87, - "source": [ - "data = []\r\n", - "\r\n", - "# get blocks\r\n", - "for line in lines:\r\n", - " object = {}\r\n", - " if \"=\" in line:\r\n", - " keytype = line.split(\" \")[0].split(\":\")\r\n", - " key = keytype[0]\r\n", - " object[\"key\"] = key\r\n", - " if len(keytype) > 1:\r\n", - " type = keytype[1]\r\n", - " object[\"type\"] = type\r\n", - " data.append(object)" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 88, - "source": [ - "words = tidy_blob.split(\" \")" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 89, - "source": [ - "def extract_objects(keywords):\r\n", - " blocks = []\r\n", - " depth = -1\r\n", - " i = -1\r\n", - " last = \"\"\r\n", - " for word in keywords:\r\n", - " #print(f\"Word: {word} | Depth: {depth} | i: {i}\")\r\n", - " if word == \"{\":\r\n", - " depth += 1\r\n", - " if depth == 1:\r\n", - " #print(\"New block of depth 1\")\r\n", - " blocks.append({\"key\": last, \"object\": []})\r\n", - " i += 1\r\n", - " elif word == \"}\":\r\n", - " depth -= 1\r\n", - " else:\r\n", - " last = word\r\n", - " if depth >= 1:\r\n", - " blocks[i][\"object\"].append(word)\r\n", - " return blocks\r\n", - "\r\n", - "data = extract_objects(words)\r\n", - "temp = []\r\n", - "for blob in data:\r\n", - " temp.append(extract_objects(blob[\"object\"]))\r\n", - "\r\n", - " \r\n", - "\r\n", - "temp" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "[[{'key': 'aa', 'object': ['{', 'aaa']},\n", - " {'key': 'ab', 'object': ['{', 'aba']},\n", - " {'key': 'ac', 'object': ['{', 'abb']}],\n", - " [{'key': 'ba', 'object': ['{']},\n", - " {'key': 'bb', 'object': ['{']},\n", - " {'key': 'bc', 'object': ['{']}],\n", - " [{'key': 'ba', 'object': ['{']},\n", - " {'key': 'bb', 'object': ['{']},\n", - " {'key': 'bc', 'object': ['{']}]]" - ] - }, - "metadata": {}, - "execution_count": 89 - } - ], - "metadata": {} - } - ], - "metadata": { - "orig_nbformat": 4, - "language_info": { - "name": "python", - "version": "3.7.8", - "mimetype": "text/x-python", - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "pygments_lexer": "ipython3", - "nbconvert_exporter": "python", - "file_extension": ".py" - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3.7.8 64-bit" - }, - "interpreter": { - "hash": "57baa5815c940fdaff4d14510622de9616cae602444507ba5d0b6727c008cbd6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/NeutrinoTestScene.json b/NeutrinoTestScene.json deleted file mode 100644 index ec593ff..0000000 --- a/NeutrinoTestScene.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"name": ["name", "Neutrino Test Scene"], "scale": ["float", 1.0], "asset_path": ["path", "./"]}, "graph": {"scene": {"0xb": ["object", {"name": ["name", "K8ZWNOQD"], "asset": "*0x7", "trans": "#0x4"}], "0xc": ["object", {"name": ["name", "US0DHKXM"], "asset": "*0x2", "trans": "#0x4"}], "0xd": ["object", {"name": ["name", "DZ53QE47"], "asset": "*0x7", "trans": "#0x4"}], "0xe": ["object", {"name": ["name", "L0MLPJ07"], "asset": "*0x9", "trans": "#0x4"}], "0xf": ["object", {"name": ["name", "RHAQ5Y6Y"], "asset": "*0x3", "trans": "#0x4"}], "0x10": ["object", {"name": ["name", "70EO9E5L"], "asset": "*0x9", "trans": "#0x4"}], "0x11": ["object", {"name": ["name", "V7QSXNME"], "asset": "*0x8", "trans": "#0x4"}], "0x12": ["object", {"name": ["name", "F60CAXPY"], "asset": "*0xa", "trans": "#0x4"}], "0x13": ["object", {"name": ["name", "FBOCRYEU"], "asset": "*0x5", "trans": "#0x4"}], "0x14": ["object", {"name": ["name", "BU7FDZ8X"], "asset": "*0x9", "trans": "#0x4"}], "0x15": ["object", {"name": ["name", "6J5HMTF0"], "asset": "*0x6", "trans": "#0x4"}], "0x16": ["object", {"name": ["name", "GWNR7BVP"], "asset": "*0xa", "trans": "#0x4"}], "0x17": ["object", {"name": ["name", "3L6PXJMP"], "asset": "*0x3", "trans": "#0x4"}], "0x18": ["object", {"name": ["name", "GFQ1ND0L"], "asset": "*0x5", "trans": "#0x4"}], "0x19": ["object", {"name": ["name", "TJOVYREC"], "asset": "*0x6", "trans": "#0x4"}], "0x1a": ["object", {"name": ["name", "8APBDYNQ"], "asset": "*0x2", "trans": "#0x4"}], "0x1b": ["object", {"name": ["name", "QEBU36A0"], "asset": "*0x3", "trans": "#0x4"}], "0x1c": ["object", {"name": ["name", "E2DONFRV"], "asset": "*0x8", "trans": "#0x4"}], "0x1d": ["object", {"name": ["name", "K266KMAB"], "asset": "*0x6", "trans": "#0x4"}], "0x1e": ["object", {"name": ["name", "YDW0I1J5"], "asset": "*0xa", "trans": "#0x4"}], "0x1f": ["object", {"name": ["name", "0CKXOWCH"], "asset": "*0x8", "trans": "#0x4"}], "0x20": ["object", {"name": ["name", "5ETNQA9W"], "asset": "*0x6", "trans": "#0x4"}], "0x21": ["object", {"name": ["name", "9OE70GMX"], "asset": "*0x1", "trans": "#0x4"}], "0x22": ["object", {"name": ["name", "R90HUM0L"], "asset": "*0x1", "trans": "#0x4"}], "0x23": ["object", {"name": ["name", "MZY8N9IZ"], "asset": "*0x6", "trans": "#0x4"}], "0x24": ["object", {"name": ["name", "NLFWVF6B"], "asset": "*0x1", "trans": "#0x4"}], "0x25": ["object", {"name": ["name", "6BA2464B"], "asset": "*0x6", "trans": "#0x4"}], "0x26": ["object", {"name": ["name", "7LNCT9LH"], "asset": "*0x3", "trans": "#0x4"}], "0x27": ["object", {"name": ["name", "A1R1LT89"], "asset": "*0xa", "trans": "#0x4"}], "0x28": ["object", {"name": ["name", "89ZWAJ9G"], "asset": "*0x1", "trans": "#0x4"}], "0x29": ["object", {"name": ["name", "IRB764Q6"], "asset": "*0x4", "trans": "#0x4"}], "0x2a": ["object", {"name": ["name", "PBR36UOB"], "asset": "*0x6", "trans": "#0x4"}], "0x2b": ["object", {"name": ["name", "I8FT701J"], "asset": "*0x7", "trans": "#0x4"}], "0x2c": ["object", {"name": ["name", "1I637ECH"], "asset": "*0x2", "trans": "#0x4"}], "0x2d": ["object", {"name": ["name", "A6UDB97O"], "asset": "*0x8", "trans": "#0x4"}], "0x2e": ["object", {"name": ["name", "0M9RLFDZ"], "asset": "*0x6", "trans": "#0x4"}], "0x2f": ["object", {"name": ["name", "IXTLUU20"], "asset": "*0x7", "trans": "#0x4"}], "0x30": ["object", {"name": ["name", "D3SL2R4M"], "asset": "*0x9", "trans": "#0x4"}], "0x31": ["object", {"name": ["name", "CIIS6Y6K"], "asset": "*0x8", "trans": "#0x4"}], "0x32": ["object", {"name": ["name", "M1XFZQJZ"], "asset": "*0xa", "trans": "#0x4"}], "0x33": ["object", {"name": ["name", "W699N3Y5"], "asset": "*0xa", "trans": "#0x4"}], "0x34": ["object", {"name": ["name", "MK4XICU6"], "asset": "*0x7", "trans": "#0x4"}], "0x35": ["object", {"name": ["name", "JH5PLU6Q"], "asset": "*0x7", "trans": "#0x4"}], "0x36": ["object", {"name": ["name", "IIUCHG2G"], "asset": "*0x4", "trans": "#0x4"}], "0x37": ["object", {"name": ["name", "Q4GXK1K4"], "asset": "*0x2", "trans": "#0x4"}], "0x38": ["object", {"name": ["name", "S623ETE1"], "asset": "*0x1", "trans": "#0x4"}], "0x39": ["object", {"name": ["name", "2065J1LS"], "asset": "*0x5", "trans": "#0x4"}], "0x3a": ["object", {"name": ["name", "ZTNA2RLO"], "asset": "*0xa", "trans": "#0x4"}], "0x3b": ["object", {"name": ["name", "SDC5B3QK"], "asset": "*0x3", "trans": "#0x4"}], "0x3c": ["object", {"name": ["name", "EGIL6IC4"], "asset": "*0x7", "trans": "#0x4"}]}, "assets": {"0x1": ["asset", {"name": ["name", "C2FK95T4"], "file": "#0x37"}], "0x2": ["asset", {"name": ["name", "L4UBCTM0"], "file": "#0x37"}], "0x3": ["asset", {"name": ["name", "JLV3H133"], "file": "#0x37"}], "0x4": ["asset", {"name": ["name", "KW5P72ZQ"], "file": "#0x37"}], "0x5": ["asset", {"name": ["name", "VMPY9TWY"], "file": "#0x37"}], "0x6": ["asset", {"name": ["name", "5UI4DUPF"], "file": "#0x37"}], "0x7": ["asset", {"name": ["name", "F9HV6WMM"], "file": "#0x37"}], "0x8": ["asset", {"name": ["name", "GUH5P1MT"], "file": "#0x37"}], "0x9": ["asset", {"name": ["name", "CMJT9GHT"], "file": "#0x37"}], "0xa": ["asset", {"name": ["name", "L0QXBOVT"], "file": "#0x37"}]}}, "cache": {"0x2": ["vec3", [0.0, 0.0, 0.0]], "0x3": ["vec3", [1.0, 1.0, 1.0]], "0x4": ["trans", {"position": "#0x2", "rotation": "#0x2", "scale": "#0x3"}], "0x37": ["path", "Assets/TestAsset.obj"]}} \ No newline at end of file diff --git a/example.neu b/example.neu deleted file mode 100644 index 26ee756..0000000 --- a/example.neu +++ /dev/null @@ -1,81 +0,0 @@ -// & declares a key pointing to object instantiated by that declaration (in this case, we're using integers in hex) -// keys are NOT indices - ultimately they should be ingested as keys to a dictionary containing each object in the Neutrino file -// * gets a reference to a key's object (Neutrino will try to preserve this reference in the target software whenever possible) -// # gets a copy from a cached object's key (Neutrino will simply replace this reference with a copy of its value - this is simply to consolidate repetitive data in-exchange) - -// overall structure: -/* -meta = {}, -graph = { - scene = {}, - assets = {} -}, -cache = {} -*/ - -meta = { - // -}, -graph = { - // note - "scene" and "assets" are essentially namespaces - // the scene itself - scene = { - &0: object = { - name: string = "SM_LargeWindow_A", - mesh: mesh = *2, - transform: trans = #b - }, - &1: object = { - name: string = "SM_LargeWindow_A2", - mesh: mesh = *2, - transform: trans = #b - } - }, - // assets used by the scene - assets { - &2: mesh { - source: path = "/Assets/Props/LargeWindowA.obj", - materials: [mat] = [*4] - }, - &4: mat { - name: string = "Simple Glass", - parent: shader = *7, - // "shader.props" is a subtype of the "shader" type, which is just a namespace that keeps it from being mixed up with other "props" subtypes (like "mesh.props", etc.) - parameters: shader.props = { - albedo: tex = *6, - roughness: float = 0.15, - normal: vec4 = #a - } - }, - &5: mat { - name: string = "Blockout Grey", - parent: shader = *7, - parameters: shader.props = { - albedo: vec4 = (0.5, 0.5, 0.5, 1.0), - roughness: float = 0.85, - normal: vec4 = #a - } - }, - &6: tex { - source: path = "/Assets/Textures/T_WindowGrime.png" - }, - &7: shader { - source: path = "/Assets/Shaders/PBRBasic.wgsl" - } - }, -}, -// anonymous objects shared by multiple other objects -cache { - // this vector is common as a position or rotation value - &8: vec3 = (0.0, 0.0, 0.0), - // this vector is common as a scale value - &9: vec3 = (1.0, 1.0, 1.0), - // this vector is common as a normal value - &a: vec4 = (0.5, 0.5, 1.0, 1.0), - // this transform is common because it's the default - &b: trans = { - position: vec3 = #8, - rotation: vec3 = #8, - scale: vec3 = #9 - } -} \ No newline at end of file diff --git a/python/NeutrinoJSONTest.ipynb b/python/NeutrinoJSONTest.ipynb new file mode 100644 index 0000000..d450c0a --- /dev/null +++ b/python/NeutrinoJSONTest.ipynb @@ -0,0 +1,131 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "source": [ + "# just returns a random string\r\n", + "import random\r\n", + "import string\r\n", + "from neutrino.encode import *\r\n", + "def random_string(length):\r\n", + " return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))\r\n", + "\r\n", + "# create test scene\r\n", + "test_scene = new_scene(name = \"Neutrino Test Scene\", cache = True)\r\n", + "\r\n", + "# populate assets\r\n", + "asset_names = []\r\n", + "for i in range(10):\r\n", + " name = random_string(8)\r\n", + " add_asset(test_scene, name, \"Assets/TestAsset.obj\")\r\n", + " asset_names.append(name)\r\n", + "\r\n", + "# populate objects in scene\r\n", + "for i in range(50):\r\n", + " spawn_object(test_scene, random_string(8), random.choice(asset_names))\r\n", + "\r\n", + "save_scene(test_scene, False)" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 2, + "source": [ + "import json\r\n", + "from neutrino.decode import *\r\n", + "\r\n", + "imported_data = json.load(open(\"NeutrinoTestScene.json\"))\r\n", + "\r\n", + "for key, value in imported_data[\"graph\"][\"scene\"].items():\r\n", + " value = (imported_data[\"cache\"][\"names\"][value[0][1:]], value[1])\r\n", + " print(value)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "['@0xa', {'@0x1': ['@0x1', 'OBK1NYBK'], '@0x4': '*0x9', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'KXKKGWH1'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'FP8K8N8K'], '@0x4': '*0x7', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'MOGF2L64'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'Z8EFSXJH'], '@0x4': '*0x8', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '1ZMPRUS6'], '@0x4': '*0x5', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'QVQSXH1U'], '@0x4': '*0x5', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'Q0F9YKF3'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'RS1HURTI'], '@0x4': '*0x3', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '9IT2UDE7'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'JJZ9VA2P'], '@0x4': '*0x3', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'PE6Y5NBE'], '@0x4': '*0x5', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '41VJPUWO'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '3D63B4QE'], '@0x4': '*0x4', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '5JZIUIZ9'], '@0x4': '*0x6', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'ZD6DD8E1'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'MIRYB8QW'], '@0x4': '*0x3', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '9HEY01NR'], '@0x4': '*0xa', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '3EK2Y8LS'], '@0x4': '*0x4', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'KJYNHF9B'], '@0x4': '*0x8', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '1WUOY60X'], '@0x4': '*0x5', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'W2L3HTUI'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'K4L6USDR'], '@0x4': '*0x6', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'ZF5RNV1N'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '62KC64SW'], '@0x4': '*0x7', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '1QCUVQII'], '@0x4': '*0x8', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '0IVRI09E'], '@0x4': '*0x3', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'J8J492ET'], '@0x4': '*0x9', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'MJJTBO2L'], '@0x4': '*0xa', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '5QAA9XJZ'], '@0x4': '*0x6', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '21GNFNFG'], '@0x4': '*0x7', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '132YJS72'], '@0x4': '*0x5', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'E7GT5ZZ0'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '26DIERE1'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'KTSB2GMU'], '@0x4': '*0x4', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'ETRS6ZK2'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '8X2ZYUGQ'], '@0x4': '*0x5', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'L09LNFXL'], '@0x4': '*0x4', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '4MHVZCEE'], '@0x4': '*0x7', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'YV5R9UAL'], '@0x4': '*0x6', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '0BRGWQ75'], '@0x4': '*0x6', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'W53N74LW'], '@0x4': '*0xa', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'S1PLWUWH'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'I7GJKHL6'], '@0x4': '*0x8', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'IPZGY627'], '@0x4': '*0xa', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '3LAE7CYV'], '@0x4': '*0x8', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'SUPYOPZB'], '@0x4': '*0x7', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', '2TSTYW4P'], '@0x4': '*0xa', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'E47HYX2O'], '@0x4': '*0x1', '@0x5': '#0x4'}]\n", + "['@0xa', {'@0x1': ['@0x1', 'KE36JYS7'], '@0x4': '*0x2', '@0x5': '#0x4'}]\n" + ] + } + ], + "metadata": {} + } + ], + "metadata": { + "orig_nbformat": 4, + "language_info": { + "name": "python", + "version": "3.7.8", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3.7.8 64-bit" + }, + "interpreter": { + "hash": "57baa5815c940fdaff4d14510622de9616cae602444507ba5d0b6727c008cbd6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/python/NeutrinoTestScene.json b/python/NeutrinoTestScene.json new file mode 100644 index 0000000..479e6c5 --- /dev/null +++ b/python/NeutrinoTestScene.json @@ -0,0 +1 @@ +{"meta": {"name": ["name", "Neutrino Test Scene"], "scale": ["float", 1.0], "asset_path": ["path", "./"]}, "graph": {"scene": {"0xb": ["@0xa", {"@0x1": ["@0x1", "OBK1NYBK"], "@0x4": "*0x9", "@0x5": "#0x4"}], "0xc": ["@0xa", {"@0x1": ["@0x1", "KXKKGWH1"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0xd": ["@0xa", {"@0x1": ["@0x1", "FP8K8N8K"], "@0x4": "*0x7", "@0x5": "#0x4"}], "0xe": ["@0xa", {"@0x1": ["@0x1", "MOGF2L64"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0xf": ["@0xa", {"@0x1": ["@0x1", "Z8EFSXJH"], "@0x4": "*0x8", "@0x5": "#0x4"}], "0x10": ["@0xa", {"@0x1": ["@0x1", "1ZMPRUS6"], "@0x4": "*0x5", "@0x5": "#0x4"}], "0x11": ["@0xa", {"@0x1": ["@0x1", "QVQSXH1U"], "@0x4": "*0x5", "@0x5": "#0x4"}], "0x12": ["@0xa", {"@0x1": ["@0x1", "Q0F9YKF3"], "@0x4": "*0x2", "@0x5": "#0x4"}], "0x13": ["@0xa", {"@0x1": ["@0x1", "RS1HURTI"], "@0x4": "*0x3", "@0x5": "#0x4"}], "0x14": ["@0xa", {"@0x1": ["@0x1", "9IT2UDE7"], "@0x4": "*0x2", "@0x5": "#0x4"}], "0x15": ["@0xa", {"@0x1": ["@0x1", "JJZ9VA2P"], "@0x4": "*0x3", "@0x5": "#0x4"}], "0x16": ["@0xa", {"@0x1": ["@0x1", "PE6Y5NBE"], "@0x4": "*0x5", "@0x5": "#0x4"}], "0x17": ["@0xa", {"@0x1": ["@0x1", "41VJPUWO"], "@0x4": "*0x2", "@0x5": "#0x4"}], "0x18": ["@0xa", {"@0x1": ["@0x1", "3D63B4QE"], "@0x4": "*0x4", "@0x5": "#0x4"}], "0x19": ["@0xa", {"@0x1": ["@0x1", "5JZIUIZ9"], "@0x4": "*0x6", "@0x5": "#0x4"}], "0x1a": ["@0xa", {"@0x1": ["@0x1", "ZD6DD8E1"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0x1b": ["@0xa", {"@0x1": ["@0x1", "MIRYB8QW"], "@0x4": "*0x3", "@0x5": "#0x4"}], "0x1c": ["@0xa", {"@0x1": ["@0x1", "9HEY01NR"], "@0x4": "*0xa", "@0x5": "#0x4"}], "0x1d": ["@0xa", {"@0x1": ["@0x1", "3EK2Y8LS"], "@0x4": "*0x4", "@0x5": "#0x4"}], "0x1e": ["@0xa", {"@0x1": ["@0x1", "KJYNHF9B"], "@0x4": "*0x8", "@0x5": "#0x4"}], "0x1f": ["@0xa", {"@0x1": ["@0x1", "1WUOY60X"], "@0x4": "*0x5", "@0x5": "#0x4"}], "0x20": ["@0xa", {"@0x1": ["@0x1", "W2L3HTUI"], "@0x4": "*0x2", "@0x5": "#0x4"}], "0x21": ["@0xa", {"@0x1": ["@0x1", "K4L6USDR"], "@0x4": "*0x6", "@0x5": "#0x4"}], "0x22": ["@0xa", {"@0x1": ["@0x1", "ZF5RNV1N"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0x23": ["@0xa", {"@0x1": ["@0x1", "62KC64SW"], "@0x4": "*0x7", "@0x5": "#0x4"}], "0x24": ["@0xa", {"@0x1": ["@0x1", "1QCUVQII"], "@0x4": "*0x8", "@0x5": "#0x4"}], "0x25": ["@0xa", {"@0x1": ["@0x1", "0IVRI09E"], "@0x4": "*0x3", "@0x5": "#0x4"}], "0x26": ["@0xa", {"@0x1": ["@0x1", "J8J492ET"], "@0x4": "*0x9", "@0x5": "#0x4"}], "0x27": ["@0xa", {"@0x1": ["@0x1", "MJJTBO2L"], "@0x4": "*0xa", "@0x5": "#0x4"}], "0x28": ["@0xa", {"@0x1": ["@0x1", "5QAA9XJZ"], "@0x4": "*0x6", "@0x5": "#0x4"}], "0x29": ["@0xa", {"@0x1": ["@0x1", "21GNFNFG"], "@0x4": "*0x7", "@0x5": "#0x4"}], "0x2a": ["@0xa", {"@0x1": ["@0x1", "132YJS72"], "@0x4": "*0x5", "@0x5": "#0x4"}], "0x2b": ["@0xa", {"@0x1": ["@0x1", "E7GT5ZZ0"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0x2c": ["@0xa", {"@0x1": ["@0x1", "26DIERE1"], "@0x4": "*0x2", "@0x5": "#0x4"}], "0x2d": ["@0xa", {"@0x1": ["@0x1", "KTSB2GMU"], "@0x4": "*0x4", "@0x5": "#0x4"}], "0x2e": ["@0xa", {"@0x1": ["@0x1", "ETRS6ZK2"], "@0x4": "*0x2", "@0x5": "#0x4"}], "0x2f": ["@0xa", {"@0x1": ["@0x1", "8X2ZYUGQ"], "@0x4": "*0x5", "@0x5": "#0x4"}], "0x30": ["@0xa", {"@0x1": ["@0x1", "L09LNFXL"], "@0x4": "*0x4", "@0x5": "#0x4"}], "0x31": ["@0xa", {"@0x1": ["@0x1", "4MHVZCEE"], "@0x4": "*0x7", "@0x5": "#0x4"}], "0x32": ["@0xa", {"@0x1": ["@0x1", "YV5R9UAL"], "@0x4": "*0x6", "@0x5": "#0x4"}], "0x33": ["@0xa", {"@0x1": ["@0x1", "0BRGWQ75"], "@0x4": "*0x6", "@0x5": "#0x4"}], "0x34": ["@0xa", {"@0x1": ["@0x1", "W53N74LW"], "@0x4": "*0xa", "@0x5": "#0x4"}], "0x35": ["@0xa", {"@0x1": ["@0x1", "S1PLWUWH"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0x36": ["@0xa", {"@0x1": ["@0x1", "I7GJKHL6"], "@0x4": "*0x8", "@0x5": "#0x4"}], "0x37": ["@0xa", {"@0x1": ["@0x1", "IPZGY627"], "@0x4": "*0xa", "@0x5": "#0x4"}], "0x38": ["@0xa", {"@0x1": ["@0x1", "3LAE7CYV"], "@0x4": "*0x8", "@0x5": "#0x4"}], "0x39": ["@0xa", {"@0x1": ["@0x1", "SUPYOPZB"], "@0x4": "*0x7", "@0x5": "#0x4"}], "0x3a": ["@0xa", {"@0x1": ["@0x1", "2TSTYW4P"], "@0x4": "*0xa", "@0x5": "#0x4"}], "0x3b": ["@0xa", {"@0x1": ["@0x1", "E47HYX2O"], "@0x4": "*0x1", "@0x5": "#0x4"}], "0x3c": ["@0xa", {"@0x1": ["@0x1", "KE36JYS7"], "@0x4": "*0x2", "@0x5": "#0x4"}]}, "assets": {"0x1": ["@0x4", {"@0x1": ["@0x1", "J7PIKN69"], "@0x2": "#0x37"}], "0x2": ["@0x4", {"@0x1": ["@0x1", "3G01JOA5"], "@0x2": "#0x37"}], "0x3": ["@0x4", {"@0x1": ["@0x1", "JSW5L3JH"], "@0x2": "#0x37"}], "0x4": ["@0x4", {"@0x1": ["@0x1", "59ONLYJX"], "@0x2": "#0x37"}], "0x5": ["@0x4", {"@0x1": ["@0x1", "50K97FJ6"], "@0x2": "#0x37"}], "0x6": ["@0x4", {"@0x1": ["@0x1", "AWZYDLB9"], "@0x2": "#0x37"}], "0x7": ["@0x4", {"@0x1": ["@0x1", "AO411BM0"], "@0x2": "#0x37"}], "0x8": ["@0x4", {"@0x1": ["@0x1", "O3ZYSC42"], "@0x2": "#0x37"}], "0x9": ["@0x4", {"@0x1": ["@0x1", "LPZZG8NE"], "@0x2": "#0x37"}], "0xa": ["@0x4", {"@0x1": ["@0x1", "426GBP10"], "@0x2": "#0x37"}]}}, "cache": {"names": {"0x1": "name", "0x2": "file", "0x3": "path", "0x4": "asset", "0x5": "transform", "0x6": "position", "0x7": "vec3", "0x8": "rotation", "0x9": "scale", "0xa": "object"}, "objects": {"0x2": ["@0x7", [0.0, 0.0, 0.0]], "0x3": ["@0x7", [1.0, 1.0, 1.0]], "0x4": ["@0x5", {"@0x6": "#0x2", "@0x8": "#0x2", "@0x9": "#0x3"}], "0x37": ["@0x3", "Assets/TestAsset.obj"]}}} \ No newline at end of file diff --git a/python/__pycache__/neutrino.cpython-37.pyc b/python/__pycache__/neutrino.cpython-37.pyc new file mode 100644 index 0000000..e9bd733 Binary files /dev/null and b/python/__pycache__/neutrino.cpython-37.pyc differ diff --git a/python/neutrino/__pycache__/decode.cpython-37.pyc b/python/neutrino/__pycache__/decode.cpython-37.pyc new file mode 100644 index 0000000..37aebe8 Binary files /dev/null and b/python/neutrino/__pycache__/decode.cpython-37.pyc differ diff --git a/python/neutrino/__pycache__/encode.cpython-37.pyc b/python/neutrino/__pycache__/encode.cpython-37.pyc new file mode 100644 index 0000000..d611f6c Binary files /dev/null and b/python/neutrino/__pycache__/encode.cpython-37.pyc differ diff --git a/python/neutrino/decode.py b/python/neutrino/decode.py new file mode 100644 index 0000000..843832c --- /dev/null +++ b/python/neutrino/decode.py @@ -0,0 +1,30 @@ +import json + +def uncache_scene(in_data): + pure_data = { + "meta": in_data["meta"], + "graph": in_data["graph"] + } + raw_json = json.dumps(pure_data) + + # cache objects + raw_cache = json.dumps(in_data["cache"]) + for key, value in in_data["cache"]["objects"].items(): + pointer = "#" + key + raw_cache = raw_cache.replace(f'"{pointer}"', json.dumps(value)) + unpacked_object_cache = json.loads(raw_cache) + + # objects + for key, value in unpacked_object_cache.items(): + print(json.dumps(value)) + pointer = "#" + key + raw_json = raw_json.replace(f'"{pointer}"', json.dumps(value)) + + # names + for key, value in in_data["cache"]["names"].items(): + raw_json = raw_json.replace("@" + key, value) + + + out_data = json.loads(raw_json) + + return out_data \ No newline at end of file diff --git a/python/neutrino/encode.py b/python/neutrino/encode.py new file mode 100644 index 0000000..6758f21 --- /dev/null +++ b/python/neutrino/encode.py @@ -0,0 +1,189 @@ +import json + +# create a new scene graph +def new_scene(name, cache = True): + # create empty neutrino data + data = { + "meta": { + "name": ("name", name), + "scale": ("float", 1.0), + "asset_path": ("path", "./") + }, + "graph": { + "scene": {}, + "assets": {} + }, + "cache": { + "names": {}, + "objects": {} + }, + "internal": { + "cache": cache, + "max_object_key": {"index": 0}, + "max_name_key": {"index": 0} + } + } + + # return that empty data + return data + +# write the data to a JSON file +def save_scene(data, readable = False): + # cache the scene + if data["internal"]["cache"]: + cache_scene(data) + + # create working copy of the scene data + clean_data = data.copy() + + # get rid of internal data (not to be exported) + del clean_data["internal"] + + filename = data["meta"]["name"][1].replace(" ", "") + ".json" + with open(filename, "w") as outfile: + if readable: + json.dump(clean_data, outfile, indent = 4) + else: + json.dump(clean_data, outfile) + +# get a new indexed object key and track it +def new_key(index): + # get the indexed key + key = hex(index["index"] + 1) + + # index the max key + index["index"] += 1 + + return key + +# returns a cached name key from a string +def name_key(data, name): + if data["internal"]["cache"]: + name_pointer = "" + + # retrieve the proper key if it exists + for key, value in data["cache"]["names"].items(): + if value == name: + name_pointer = key + + # if the name pointer is still empty, make a new key and add it to the cache + if name_pointer == "": + name_pointer = new_key(data["internal"]["max_name_key"]) + data["cache"]["names"][name_pointer] = name + + return "@" + name_pointer + else: + return name + +# add an asset to the graph +def add_asset(data, name, path): + asset_data = { + name_key(data, "name"): (name_key(data, "name"), name), + name_key(data, "file"): (name_key(data, "path"), path) + } + + # add the asset to the graph + data["graph"]["assets"][new_key(data["internal"]["max_object_key"])] = (name_key(data, "asset"), asset_data) + +# add an object to the scene +def spawn_object(data, name, asset): + object_data = { + name_key(data, "name"): (name_key(data, "name"), name), + name_key(data, "asset"): "", + name_key(data, "transform"): (name_key(data, "transform"), { + name_key(data, "position"): (name_key(data, "vec3"), [0.0, 0.0, 0.0]), + name_key(data, "rotation"): (name_key(data, "vec3"), [0.0, 0.0, 0.0]), + name_key(data, "scale"): (name_key(data, "vec3"), [1.0, 1.0, 1.0]) + }) + } + + # get an asset key by the provided name + for key, value in data["graph"]["assets"].items(): + if value[1][name_key(data, "name")][1] == asset: + object_data[name_key(data, "asset")] = f"*{key}" + + # add the object to the scene + data["graph"]["scene"][new_key(data["internal"]["max_object_key"])] = (name_key(data, "object"), object_data) + +# recursively cache a single typeval tuple object +def cache_typeval(cache, typeval): + # ignore if not typeval + if type(typeval) == tuple: + for key, value in typeval[1].items(): + # refuse to cache pointers (that's just... that would just be a nightmare) + if type(value) == str: + is_pointer = ("*" in value) + else: + is_pointer = False + if not is_pointer: + # cache member objects if it's a dictionary object + if type(value[1]) == dict: + cache_typeval(cache, value) + + value_hash = hash(str(value)) + + # track in cache + if value_hash not in cache["objects"]: + cache_pointer = new_key(cache["key_index"]) + cache["objects"][value_hash] = {"key": cache_pointer, "value": value, "count": 1} + else: + cache_pointer = cache["objects"][value_hash]["key"] + cache["objects"][value_hash]["count"] += 1 + + # replace real value with hash + typeval[1][key] = "#" + cache_pointer + +# if there's only one instance of a certain value, convert it back to the original value and destroy the cached version +def uncache_typeval(cache, typeval): + for key, value in typeval[1].items(): + # refuse to cache pointers (that's just... that would just be a nightmare) + if type(value) == str: + is_pointer = ("*" in value) + else: + is_pointer = False + if not is_pointer: + # cache member objects if it's a dictionary object + if type(value[1]) == dict: + uncache_typeval(cache, value) + + value_hash = hash(str(value)) + + # check if it occurs only once + cache_key = value.replace("#", "") + if cache[cache_key]["count"] <= 1: + # replace the cache pointer in the scene data with its original value + typeval[1][key] = cache[cache_key]["value"] + + # delete this object from the cache + del cache[cache_key] + +# cache the scene +def cache_scene(data): + containers = [ + data["graph"]["scene"], + data["graph"]["assets"] + ] + + # build a cache of value hashes and pointers + hash_cache = {"key_index": {"index": 0}, "objects": {}} + for objects in containers: + for key, value in objects.items(): + cache_typeval(hash_cache, value) + + # create a cache hashed with pointer keys instead of value hashes + key_cache = {} + for key, value in hash_cache["objects"].items(): + key_cache[value["key"]] = {"value": value["value"], "count": value["count"]} + + # prune the cache to only redirect repeat values + for objects in containers: + for key, value in objects.items(): + uncache_typeval(key_cache, value) + + # create a serialized cache usable by neutrino + serial_cache = {} + for key, value in key_cache.items(): + serial_cache[key] = value["value"] + + # add that cache to the neutrino scene data + data["cache"]["objects"] = serial_cache \ No newline at end of file