305 lines
15 KiB
Plaintext
305 lines
15 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 157,
|
|
"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):\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",
|
|
" json.dump(clean_data, outfile, indent = 4)\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": "code",
|
|
"execution_count": 158,
|
|
"source": [
|
|
"# 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",
|
|
" for objects in containers:\r\n",
|
|
" # temp cache\r\n",
|
|
" hash_cache = {}\r\n",
|
|
"\r\n",
|
|
" # hash all values\r\n",
|
|
" for key, value in objects.items():\r\n",
|
|
" for key, value in value[1].items():\r\n",
|
|
" # ignore pointers (the only non-tuple object)\r\n",
|
|
" if type(value) == tuple:\r\n",
|
|
" # convert into string and hash that\r\n",
|
|
" value_hash = hash(str(value))\r\n",
|
|
"\r\n",
|
|
" # track in temp cache\r\n",
|
|
" if value_hash not in hash_cache:\r\n",
|
|
" hash_cache[value_hash] = {\"value\": value, \"count\": 1}\r\n",
|
|
" else:\r\n",
|
|
" hash_cache[value_hash][\"count\"] += 1\r\n",
|
|
"\r\n",
|
|
" # throw out all non-repeated values\r\n",
|
|
" bad_keys = []\r\n",
|
|
" for key, value in hash_cache.items():\r\n",
|
|
" if value[\"count\"] < 2:\r\n",
|
|
" bad_keys.append(key)\r\n",
|
|
" for key in bad_keys:\r\n",
|
|
" del hash_cache[key]\r\n",
|
|
"\r\n",
|
|
" # create hash objects for each repeated value\r\n",
|
|
" for key, value in hash_cache.items():\r\n",
|
|
" cache_pointer = cache_key(data)\r\n",
|
|
" data[\"cache\"][cache_pointer] = value[\"value\"]\r\n",
|
|
" hash_cache[key][\"pointer\"] = cache_pointer\r\n",
|
|
"\r\n",
|
|
" # replace all instances of cached values in the graph with corresponding cache pointers\r\n",
|
|
" for object_key, object_value in objects.items():\r\n",
|
|
" for value_key, value_value in object_value[1].items():\r\n",
|
|
" # ignore pointers (the only non-tuple object)\r\n",
|
|
" if type(value_value) == tuple:\r\n",
|
|
" # convert into string and hash that\r\n",
|
|
" value_hash = hash(str(value_value))\r\n",
|
|
"\r\n",
|
|
" # if this value is cached, replace it with its cache pointer\r\n",
|
|
" if value_hash in hash_cache:\r\n",
|
|
" objects[object_key][1][value_key] = \"#\" + hash_cache[value_hash][\"pointer\"]"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 159,
|
|
"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\r\n",
|
|
"\r\n",
|
|
" # DEBUG\r\n",
|
|
" with open(\"hash_cache.json\", \"w\") as outfile:\r\n",
|
|
" json.dump(hash_cache, outfile, indent = 4)\r\n",
|
|
" with open(\"key_cache.json\", \"w\") as outfile:\r\n",
|
|
" json.dump(key_cache, outfile, indent = 4)\r\n",
|
|
" with open(\"serial_cache.json\", \"w\") as outfile:\r\n",
|
|
" json.dump(serial_cache, outfile, indent = 4)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 160,
|
|
"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(3):\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(5):\r\n",
|
|
" spawn_object(test_scene, random_string(8), random.choice(asset_names))\r\n",
|
|
"\r\n",
|
|
"# cache the scene\r\n",
|
|
"cache_scene(test_scene)\r\n",
|
|
"\r\n",
|
|
"save_scene(test_scene)"
|
|
],
|
|
"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
|
|
} |