doc(import_bsi): Improve comments
Signed-off-by: Lucas Schwiderski <lucas@lschwiderski.de>
This commit is contained in:
parent
80a8e239d3
commit
1c6d160a1a
1 changed files with 31 additions and 21 deletions
|
@ -20,15 +20,14 @@ import json
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
|
|
||||||
from bpy_extras.io_utils import unpack_list
|
|
||||||
|
|
||||||
|
|
||||||
def parse_sjson(file_path, skip_editor_data=True):
|
def parse_sjson(file_path, skip_editor_data=True):
|
||||||
"""
|
"""
|
||||||
Translate Bitsquid .bsi SJSON data to plain JSON,
|
Translate Bitsquid .bsi SJSON data to plain JSON,
|
||||||
then parse into a python dictionary.
|
then parse into a python dictionary.
|
||||||
|
|
||||||
Taken from `bsi_import` in the Vermintide 2 SDK.
|
Taken from `bsi_import` in the Vermintide 2 SDK, but slightly
|
||||||
|
modified to fix some issues and improve readability.
|
||||||
"""
|
"""
|
||||||
return_dict = {}
|
return_dict = {}
|
||||||
try:
|
try:
|
||||||
|
@ -167,16 +166,26 @@ def parse_sjson(file_path, skip_editor_data=True):
|
||||||
|
|
||||||
|
|
||||||
def find(arr, f):
|
def find(arr, f):
|
||||||
|
"""
|
||||||
|
Find a value in a list by executing `f`
|
||||||
|
on each item until it returns `true`.
|
||||||
|
"""
|
||||||
for key, val in arr.items():
|
for key, val in arr.items():
|
||||||
if f(val, key):
|
if f(val, key):
|
||||||
return val, key
|
return val, key
|
||||||
|
|
||||||
|
|
||||||
def create_object(self, context, name, node_data, geometries):
|
def create_object(self, context, name, node_data, geometries):
|
||||||
|
"""
|
||||||
|
Create a Blender object from a BSI node definition
|
||||||
|
and additional data from the file.
|
||||||
|
"""
|
||||||
print("[create_object]", name, node_data)
|
print("[create_object]", name, node_data)
|
||||||
|
# A list of tuples, where each tuple represents a Vector3(x, y, z).
|
||||||
vertices = []
|
vertices = []
|
||||||
|
# A list of tuples, where each tuple contains three indices into `vertices`.
|
||||||
|
# Those three indices define the vertices that make up the face.
|
||||||
faces = []
|
faces = []
|
||||||
edges = []
|
|
||||||
|
|
||||||
tri_count = int(geometries["indices"]["size"]) / 3
|
tri_count = int(geometries["indices"]["size"]) / 3
|
||||||
|
|
||||||
|
@ -187,6 +196,10 @@ def create_object(self, context, name, node_data, geometries):
|
||||||
for channel in data_stream['channels']:
|
for channel in data_stream['channels']:
|
||||||
stream_data = data_stream["data"]
|
stream_data = data_stream["data"]
|
||||||
if channel['name'] == 'POSITION':
|
if channel['name'] == 'POSITION':
|
||||||
|
# NOTE: Do we need to handle `stride != 3`?
|
||||||
|
# Since the value seems to be fixed per stream, a higher
|
||||||
|
# stride would only be possible for objects that can be built
|
||||||
|
# entirely from quads, which is very uncommon.
|
||||||
if stride != 3:
|
if stride != 3:
|
||||||
raise RuntimeError("stride != 3 cannot be handled")
|
raise RuntimeError("stride != 3 cannot be handled")
|
||||||
|
|
||||||
|
@ -209,38 +222,36 @@ def create_object(self, context, name, node_data, geometries):
|
||||||
index_stream[j + 2],
|
index_stream[j + 2],
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
|
# TODO: Implement other channel types
|
||||||
self.report(
|
self.report(
|
||||||
{'WARNING'},
|
{'WARNING'},
|
||||||
"Unknown channel name: {}".format(channel["name"])
|
"Unknown channel type: {}".format(channel["name"])
|
||||||
)
|
)
|
||||||
|
|
||||||
print(vertices, faces)
|
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(name)
|
mesh = bpy.data.meshes.new(name)
|
||||||
mesh.from_pydata(vertices, edges, faces)
|
mesh.from_pydata(vertices, [], faces)
|
||||||
|
|
||||||
return mesh
|
return mesh
|
||||||
|
|
||||||
|
|
||||||
def import_node(self, context, name, node_data, global_data):
|
def import_node(self, context, name, node_data, global_data):
|
||||||
|
"""Import a BSI node. Recurses into child nodes."""
|
||||||
print("[import_node]", name, node_data)
|
print("[import_node]", name, node_data)
|
||||||
if "geometries" in node_data:
|
if "geometries" in node_data:
|
||||||
geometry_name = node_data["geometries"][0]
|
geometry_name = node_data["geometries"][0]
|
||||||
print("[import_node] Building geometry '{}' for object '{}'".format(geometry_name, name))
|
print("[import_node] Building geometry '{}' for object '{}'".format(geometry_name, name))
|
||||||
geometries = global_data["geometries"][geometry_name]
|
geometries = global_data["geometries"][geometry_name]
|
||||||
mesh = create_object(self, context, name, node_data, geometries)
|
mesh = create_object(self, context, name, node_data, geometries)
|
||||||
print(mesh)
|
|
||||||
|
|
||||||
obj = bpy.data.objects.new(mesh.name, mesh)
|
obj = bpy.data.objects.new(mesh.name, mesh)
|
||||||
# TODO: Apply `local` tranformation
|
|
||||||
else:
|
else:
|
||||||
print("[import_node] Adding empty for '{}'".format(name))
|
print("[import_node] Adding empty for '{}'".format(name))
|
||||||
# TODO: Extract position and rotation from `local` matrix,
|
|
||||||
# if it's not the identity matrix
|
|
||||||
# NOTE: Low priority, Fatshark seems to stick with
|
|
||||||
# identity matrices here
|
|
||||||
obj = bpy.data.objects.new(name, None)
|
obj = bpy.data.objects.new(name, None)
|
||||||
|
|
||||||
|
# Needs to happen before scaling can be applied.
|
||||||
|
# TODO: Check if above is true, was seen on Stackoverflow.
|
||||||
|
obj.matrix_world = mathutils.Matrix()
|
||||||
|
# TODO: Apply tranformation matrix in `node_data["local"]`
|
||||||
|
|
||||||
if "children" in node_data:
|
if "children" in node_data:
|
||||||
for child_name, child_data in node_data["children"].items():
|
for child_name, child_data in node_data["children"].items():
|
||||||
if child_data["parent"] != name:
|
if child_data["parent"] != name:
|
||||||
|
@ -258,7 +269,8 @@ def import_node(self, context, name, node_data, global_data):
|
||||||
)
|
)
|
||||||
child_obj.parent = obj
|
child_obj.parent = obj
|
||||||
|
|
||||||
# Make sure all objects are linked to the current collection
|
# Make sure all objects are linked to the current collection.
|
||||||
|
# Otherwise they won't show up in the outliner.
|
||||||
collection = context.collection
|
collection = context.collection
|
||||||
collection.objects.link(obj)
|
collection.objects.link(obj)
|
||||||
return obj
|
return obj
|
||||||
|
@ -275,15 +287,13 @@ def load(self, context, filepath, *, relpath=None):
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
view_layer = context.view_layer
|
view_layer = context.view_layer
|
||||||
global_matrix = mathutils.Matrix()
|
|
||||||
|
|
||||||
for name, node_data in global_data["nodes"].items():
|
for name, node_data in global_data["nodes"].items():
|
||||||
obj = import_node(self, context, name, node_data, global_data)
|
obj = import_node(self, context, name, node_data, global_data)
|
||||||
|
|
||||||
view_layer.objects.active = obj
|
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
# One of the objects should be set as active.
|
||||||
# we could apply this anywhere before scaling.
|
# This is the easiest to implement and perfectly fine.
|
||||||
obj.matrix_world = global_matrix
|
view_layer.objects.active = obj
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
Loading…
Add table
Reference in a new issue