Skip to content

Commit

Permalink
X
Browse files Browse the repository at this point in the history
X
  • Loading branch information
Aitolda committed Sep 5, 2024
1 parent 7ebdb68 commit 785a271
Show file tree
Hide file tree
Showing 17 changed files with 1,304 additions and 62 deletions.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
139 changes: 123 additions & 16 deletions blender/vircadia_blender_world_tools/import_export/json_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
def get_vircadia_entity_data(obj, content_path):
entity_data = {}

entity_data["name"] = obj.get("name", obj.name)
entity_type = obj.get("type", "Entity")
entity_data["type"] = entity_type

Expand All @@ -21,48 +20,156 @@ def get_vircadia_entity_data(obj, content_path):

if obj.rotation_mode == 'QUATERNION':
blender_rot = obj.rotation_quaternion
vircadia_rot = coordinate_utils.blender_to_vircadia_rotation(*blender_rot)
entity_data["rotation"] = {"x": vircadia_rot[0], "y": vircadia_rot[1], "z": vircadia_rot[2], "w": vircadia_rot[3]}
else:
blender_rot = obj.rotation_euler.to_quaternion()
vircadia_rot = coordinate_utils.blender_to_vircadia_rotation(*blender_rot)
entity_data["rotation"] = {"x": vircadia_rot[0], "y": vircadia_rot[1], "z": vircadia_rot[2], "w": vircadia_rot[3]}
vircadia_rot = coordinate_utils.blender_to_vircadia_rotation(*blender_rot)
entity_data["rotation"] = {"x": vircadia_rot[0], "y": vircadia_rot[1], "z": vircadia_rot[2], "w": vircadia_rot[3]}

custom_props = property_utils.get_custom_properties(obj)
entity_data.update(custom_props)
entity_data.update(reconstruct_entity_properties(custom_props, entity_type))

# Add or update GLTF reference for appropriate entity types
if entity_type.lower() in ["model", "shape"]:
gltf_url = urljoin(content_path, "environment.gltf")
entity_data["modelURL"] = gltf_url
if entity_type.lower() == "zone":
entity_data["userData"] = construct_zone_user_data(obj)

return entity_data

def reconstruct_entity_properties(props, entity_type):
reconstructed = {}
for key, value in props.items():
if key in ["position", "dimensions", "rotation", "type"]:
continue # These are handled separately

parts = key.split('_')
current = reconstructed
for i, part in enumerate(parts[:-1]):
if part not in current:
current[part] = {}
current = current[part]

final_key = parts[-1]
if final_key in ['red', 'green', 'blue']:
if 'color' not in current:
current['color'] = {}
current['color'][final_key] = value
elif final_key in ['x', 'y', 'z', 'w']:
if 'direction' not in current:
current['direction'] = {}
current['direction'][final_key] = value
elif final_key.lower().endswith('mode') and final_key.lower() != 'model':
current[final_key] = "enabled" if value else "disabled"
else:
current[final_key] = value

# Handle specific entity type structures
if entity_type.lower() == "zone":
for mode in ["keyLightMode", "ambientLightMode", "skyboxMode", "hazeMode", "bloomMode"]:
if mode not in reconstructed:
reconstructed[mode] = "enabled" if reconstructed.get(mode.replace("Mode", "")) else "disabled"

return reconstructed

def construct_zone_user_data(obj):
user_data = {
"renderingPipeline": {
"fxaaEnabled": obj.get("renderingPipeline_fxaaEnabled", True),
"glowLayerEnabled": obj.get("renderingPipeline_glowLayerEnabled", True),
"glowLayer": {
"blurKernelSize": obj.get("renderingPipeline_glowLayer_blurKernelSize", 16),
"intensity": obj.get("renderingPipeline_glowLayer_intensity", 1.5)
}
},
"environment": {
"environmentTexture": obj.get("environment_environmentTexture", "")
}
}
return json.dumps(user_data)

def load_entity_template(entity_type):
addon_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

# Map entity types to their corresponding template files
template_map = {
"box": "shapeOnly",
"sphere": "shapeOnly",
"shape": "shapeOnly",
# Add other shape types here as needed
}

# Get the correct template name, defaulting to the entity type if not in the map
template_name = template_map.get(entity_type.lower(), entity_type.lower())

template_path = os.path.join(addon_path, "json_library", f"models_{template_name}Only.json")

try:
with open(template_path, 'r') as f:
return json.load(f)
except FileNotFoundError:
print(f"Warning: Template file not found for entity type '{entity_type}'. Using generic entity template.")
# Provide a basic generic template as fallback
return {"Entities": [{"type": entity_type}]}

def export_vircadia_json(context, filepath):
scene = context.scene
content_path = scene.vircadia_content_path

if not content_path:
raise ValueError("Content Path is not set. Please set it in the Vircadia panel before exporting.")

# Ensure the content path ends with a slash for proper URL joining
if not content_path.endswith('/'):
content_path += '/'

scene_data = {"Entities": []}

# Add the Model entity from the template
model_template = load_entity_template("model")
model_entity = model_template["Entities"][0]
model_entity["modelURL"] = urljoin(content_path, "environment.glb")
scene_data["Entities"].append(model_entity)

for obj in bpy.data.objects:
if "name" in obj:
entity_data = get_vircadia_entity_data(obj, content_path)
scene_data["Entities"].append(entity_data)
if "type" in obj:
entity_type = obj["type"].lower()
if entity_type not in ["light", "model"]:
entity_data = get_vircadia_entity_data(obj, content_path)

# Merge with template
template = load_entity_template(entity_type)
template_entity = template["Entities"][0]
merged_entity = {**template_entity, **entity_data}

scene_data["Entities"].append(merged_entity)

os.makedirs(os.path.dirname(filepath), exist_ok=True)

with open(filepath, 'w') as f:
json.dump(scene_data, f, indent=2)

print(f"Vircadia JSON exported successfully to {filepath}")

class EXPORT_OT_vircadia_json(bpy.types.Operator):
bl_idname = "export_scene.vircadia_json"
bl_label = "Export Vircadia JSON"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")

def execute(self, context):
if not self.filepath:
blend_filepath = context.blend_data.filepath
if not blend_filepath:
blend_filepath = "untitled"
else:
blend_filepath = os.path.splitext(blend_filepath)[0]
self.filepath = os.path.join(os.path.dirname(blend_filepath), "models.json")

export_vircadia_json(context, self.filepath)
return {'FINISHED'}

def invoke(self, context, event):
self.filepath = "models.json"
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}

def register():
pass
bpy.utils.register_class(EXPORT_OT_vircadia_json)

def unregister():
pass
bpy.utils.unregister_class(EXPORT_OT_vircadia_json)
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,48 @@ def load_json(file_path):
def import_entities(data, json_directory):
zone_obj = None
for entity in data.get("Entities", []):
# Ensure "type" has only the first letter capitalized
if "type" in entity:
entity["type"] = entity["type"].title()

# Ensure "shape" has only the first letter capitalized if it exists
if "shape" in entity:
entity["shape"] = entity["shape"].title()

obj = object_creation.create_blender_object(entity)
if obj is not None:
# Special handling for zone objects
if entity.get("type", "").lower() == "zone":
obj["name"] = "zone"
if entity.get("type") == "Zone":
obj["name"] = "Zone"
zone_obj = obj

# Set custom properties
property_utils.set_custom_properties(obj, entity)

# Additional check to ensure zone objects always have "zone" as their name custom property
# Additional check to ensure zone objects always have "Zone" as their name custom property
if obj.get("type") == "Zone":
obj["name"] = "zone"
obj["name"] = "Zone"

# Move the object to the appropriate collection
move_to_type_collection(obj, entity.get("type", "Unknown"))

# Add transform update handler
bpy.app.handlers.depsgraph_update_post.append(object_creation.create_transform_update_handler(obj))
else:
error_handling.log_import_error(entity)
return zone_obj

def move_to_type_collection(obj, entity_type):
# Get or create the collection for this entity type
collection = collection_utils.get_or_create_collection(entity_type)

# Remove the object from all other collections
for coll in obj.users_collection:
coll.objects.unlink(obj)

# Link the object to the type-specific collection
collection.objects.link(obj)

def process_vircadia_json(file_path):
data = load_json(file_path)
if data is None:
Expand All @@ -52,6 +74,10 @@ def process_vircadia_json(file_path):
error_handling.log_warning("No zone entity found in the imported data.")

world_setup.configure_world_and_viewport()

# Clean up empty collections
collection_utils.remove_empty_collections()

print("Vircadia entities imported successfully.")

def register():
Expand Down
Loading

0 comments on commit 785a271

Please sign in to comment.