Skip to content

Commit

Permalink
Sync with private repo commit 1
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Sep 2, 2023
1 parent 5a56c00 commit 734b4df
Show file tree
Hide file tree
Showing 12 changed files with 1,966 additions and 680 deletions.
95 changes: 84 additions & 11 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ==============================================================================
# Copyright (c) 2022 Thomas Mathieson.
# Copyright (c) 2022-2023 Thomas Mathieson.
# ==============================================================================

# ##### BEGIN GPL LICENSE BLOCK #####
Expand All @@ -24,7 +24,7 @@
bl_info = {
"name": "Import OMSI map/cfg/sco/o3d files",
"author": "Adam/Thomas Mathieson",
"version": (1, 1, 4),
"version": (1, 2, 2),
"blender": (3, 1, 0),
"location": "File > Import-Export",
"description": "Import OMSI model .map, .cfg, .sco, and .o3d files along with their meshes, UVs, and materials",
Expand All @@ -35,7 +35,7 @@
"category": "Import-Export"
}

from .o3d_io import io_o3d_import, io_o3d_export, io_omsi_tile
from .o3d_io import io_o3d_import, io_o3d_export, io_omsi_tile, io_omsi_map_panel
import bpy
from mathutils import Matrix

Expand Down Expand Up @@ -91,6 +91,13 @@ class ImportModelCFG(bpy.types.Operator, ImportHelper):
# ImportHelper mixin class uses this
filename_ext = ".o3d"

import_custom_normals = BoolProperty(
name="Import custom normals",
description="Import the mesh normals as Blender custom split normals. This allows the original normal data to "
"be preserved correctly but can be harder to edit later.",
default=True
)

filter_glob = StringProperty(
default="*.cfg;*.sco;*.o3d;*.rdy",
options={'HIDDEN'},
Expand Down Expand Up @@ -120,6 +127,13 @@ class ImportModelCFG(bpy.types.Operator, ImportHelper):
default=True,
)

parent_collection = StringProperty(
name="Parent collection (leave blank for default)",
description="When set, this will place all the imported objects in the specified collection. If a collection "
"with the specified name does not exist, it will be created.",
default=""
)

# Selected files
files = CollectionProperty(type=bpy.types.PropertyGroup)

Expand All @@ -130,7 +144,25 @@ def execute(self, context):
:return: success message
"""
context.window.cursor_set('WAIT')
io_o3d_import.do_import(self.filepath, context, self.import_x, self.override_text_encoding, self.hide_lods)

# Find or create the parent collection
parent_collection = None
if self.parent_collection != "":
if bpy.app.version < (2, 80):
if self.parent_collection not in bpy.data.groups:
parent_collection = bpy.data.groups.new(self.parent_collection)
else:
parent_collection = bpy.data.groups[self.parent_collection]
else:
if self.parent_collection not in bpy.data.collections:
parent_collection = bpy.data.collections.new(self.parent_collection)
bpy.context.scene.collection.children.link(parent_collection)
else:
parent_collection = bpy.data.collections[self.parent_collection]

io_o3d_import.do_import(self.filepath, context, self.import_x, self.override_text_encoding, self.hide_lods,
import_lods=True, parent_collection=parent_collection,
split_normals=self.import_custom_normals)
context.window.cursor_set('DEFAULT')

return {'FINISHED'}
Expand All @@ -150,6 +182,12 @@ class ExportModelCFG(bpy.types.Operator, ExportHelper):
default="*.cfg;*.sco;*.o3d",
options={'HIDDEN'},
)
export_custom_normals = BoolProperty(
name="Export custom normals",
description="Export Blender custom split normals as the mesh normals. This allows for higher fidelity normals "
"to be exported.",
default=True
)
use_selection = BoolProperty(
name="Selection Only",
description="Export selected objects only",
Expand All @@ -172,7 +210,8 @@ def execute(self, context):
context.window.cursor_set('WAIT')

global_matrix = Matrix.Scale(self.global_scale, 4)
io_o3d_export.do_export(self.filepath, context, global_matrix, self.use_selection, self.o3d_version)
io_o3d_export.do_export(self.filepath, context, global_matrix, self.use_selection, self.o3d_version,
self.export_custom_normals)

context.window.cursor_set('DEFAULT')

Expand Down Expand Up @@ -238,6 +277,25 @@ class ImportOMSITile(bpy.types.Operator, ImportHelper):
default=True,
)

centre_x = IntProperty(
name="Centre Tile X",
description="When loading a global.cfg file, the x coordinate of the first tile to load.",
default=0
)
centre_y = IntProperty(
name="Centre Tile Y",
description="When loading a global.cfg file, the y coordinate of the first tile to load.",
default=0
)
load_radius = IntProperty(
name="Load Radius",
description="When loading a global.cfg file, how many tiles around the centre tile to load. Set to 0 to only "
"load the centre tile, 1 loads the centre tile and it's 4 neighbours, 2 loads the centre tile and "
"it's 8 neighbours...",
default=999999,
min=0
)

# Selected files
files = CollectionProperty(type=bpy.types.PropertyGroup)

Expand All @@ -249,7 +307,7 @@ def execute(self, context):
"""
context.window.cursor_set('WAIT')
io_omsi_tile.do_import(context, self.filepath, self.import_scos, self.import_splines, self.spline_tess_dist,
self.spline_curve_sag, self.import_x)
self.spline_curve_sag, self.import_x, self.centre_x, self.centre_y, self.load_radius)
context.window.cursor_set('DEFAULT')

return {'FINISHED'}
Expand All @@ -275,9 +333,17 @@ def menu_func_import_tile(self, context):


def register():
for cls in classes:
cls = make_annotations(cls)
bpy.utils.register_class(cls)
all_classes = classes[:]
all_classes.extend(io_omsi_map_panel.get_classes())
log("Registering Blender-O3D-IO version: {0}...".format(bl_info["version"]))
for cls in all_classes:
try:
cls = make_annotations(cls)
bpy.utils.register_class(cls)
except:
log("Failed to register {0}".format(cls))

io_omsi_map_panel.register()

# Compat with 2.7x and 3.x
if bpy.app.version[0] < 3 and bpy.app.version[1] < 80:
Expand All @@ -291,8 +357,15 @@ def register():


def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
all_classes = classes[:]
all_classes.extend(io_omsi_map_panel.get_classes())
for cls in all_classes[::-1]:
try:
bpy.utils.unregister_class(cls)
except:
log("Failed to unregister {0}".format(cls))

io_omsi_map_panel.unregister()

# Compat with 2.7x and 3.x
if bpy.app.version[0] < 3 and bpy.app.version[1] < 80:
Expand Down
2 changes: 1 addition & 1 deletion o3d_io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# ==============================================================================
# Copyright (c) 2022 Thomas Mathieson.
# Copyright (c) 2022-2023 Thomas Mathieson.
# ==============================================================================
18 changes: 16 additions & 2 deletions o3d_io/blender_texture_io.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ==============================================================================
# Copyright (c) 2022 Thomas Mathieson.
# Copyright (c) 2022-2023 Thomas Mathieson.
# ==============================================================================

import os
Expand Down Expand Up @@ -29,7 +29,7 @@ def __init__(self, image):
self.texture = TextureSlotWrapper.TextureWrapper(image)


def load_image(base_file_path, texture_path, abs_path=False):
def find_image_path(base_file_path, texture_path, abs_path=False, find_cfg=False):
if base_file_path[-3:] == "sco":
tex_file = os.path.join(os.path.dirname(base_file_path), "texture", texture_path.lower())
elif base_file_path[-3:] == "map":
Expand Down Expand Up @@ -63,11 +63,25 @@ def load_image(base_file_path, texture_path, abs_path=False):
if os.path.ismount(tex_dir) or last_dir == tex_dir:
break

if find_cfg:
tex_file += ".cfg"

if os.path.isfile(tex_file):
return tex_file
else:
return None


def load_image(base_file_path, texture_path, abs_path=False):
tex_file = find_image_path(base_file_path, texture_path, abs_path, False)

if tex_file is not None and os.path.isfile(tex_file):
# TODO: Alpha_8_UNORM DDS files are not supported by Blender
image = bpy.data.images.load(tex_file,
check_existing=True)

is_dds = tex_file[:-3] == "dds"

if not image.has_data and is_dds:
# image.has_data doesn't necessarily mean Blender can't load it (sometimes it's deferred), but there's a
# good chance it failed.
Expand Down
4 changes: 4 additions & 0 deletions o3d_io/dds_loader/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# ==============================================================================
# Copyright (c) 2023 Thomas Mathieson.
# ==============================================================================

39 changes: 24 additions & 15 deletions o3d_io/io_o3d_export.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ==============================================================================
# Copyright (c) 2022 Thomas Mathieson.
# Copyright (c) 2022-2023 Thomas Mathieson.
# ==============================================================================

import os
Expand All @@ -20,7 +20,7 @@ def log(*args):
print("[O3D_Export]", *args)


def export_mesh(filepath, context, blender_obj, mesh, transform_matrix, materials, o3d_version):
def export_mesh(filepath, context, blender_obj, mesh, transform_matrix, materials, o3d_version, export_custom_normals):
# Create o3d file
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, "wb") as f:
Expand Down Expand Up @@ -52,10 +52,10 @@ def export_mesh(filepath, context, blender_obj, mesh, transform_matrix, material
else:
v_uv = (0, 0)

if (v_co, v_nrm) in vert_map:
face_inds.append(vert_map[(v_co, v_nrm)])
if (v_co, v_nrm, v_uv) in vert_map:
face_inds.append(vert_map[(v_co, v_nrm, v_uv)])
else:
vert_map[(v_co, v_nrm)] = vert_count
vert_map[(v_co, v_nrm, v_uv)] = vert_count
verts.append(
[v_co[0], v_co[1], v_co[2],
v_nrm[0], v_nrm[1], v_nrm[2],
Expand All @@ -77,26 +77,32 @@ def export_mesh(filepath, context, blender_obj, mesh, transform_matrix, material
tris.append((face_inds[1], face_inds[3], face_inds[2], face.material_index))
else:
mesh.calc_loop_triangles()
if export_custom_normals and mesh.has_custom_normals:
# mesh.polygons.foreach_set("use_smooth", [False] * len(mesh.polygons))
mesh.use_auto_smooth = True
else:
mesh.free_normals_split()
mesh.calc_normals_split()
for tri_loop in mesh.loop_triangles:
tri = []
tris.append(tri)

for tri_vert, loop in zip(tri_loop.vertices, tri_loop.loops):
for tri_vert, loop, normal in zip(tri_loop.vertices, tri_loop.loops, tri_loop.split_normals):
vert = mesh.vertices[tri_vert]
v_co = vert.co[:]
v_nrm = vert.normal[:]
v_nrm = mesh.loops[loop].normal[:]
if uv_layer is not None:
v_uv = uv_layer[loop].uv[:2]
else:
v_uv = (0, 0)

if (v_co, v_nrm) in vert_map:
tri.append(vert_map[(v_co, v_nrm)])
if (v_co, v_nrm, v_uv) in vert_map:
tri.append(vert_map[(v_co, v_nrm, v_uv)])
else:
vert_map[(v_co, v_nrm)] = vert_count
vert_map[(v_co, v_nrm, v_uv)] = vert_count
verts.append(
[v_co[0], v_co[1], v_co[2],
v_nrm[0], v_nrm[1], v_nrm[2],
-v_nrm[0], -v_nrm[1], -v_nrm[2],
v_uv[0], 1 - v_uv[1]])
tri.append(vert_count)
vert_count += 1
Expand Down Expand Up @@ -159,7 +165,7 @@ def export_mesh(filepath, context, blender_obj, mesh, transform_matrix, material
invert_triangle_winding=True)


def do_export(filepath, context, global_matrix, use_selection, o3d_version):
def do_export(filepath, context, global_matrix, use_selection, o3d_version, export_custom_normals=True):
"""
Exports the selected CFG/SCO/O3D file
:param o3d_version: O3D version to export the file as
Expand Down Expand Up @@ -224,15 +230,17 @@ def do_export(filepath, context, global_matrix, use_selection, o3d_version):
))

if bpy.app.version < (2, 80):
o3d_matrix = axis_conversion_matrix * ob.matrix_world
o3d_matrix = axis_conversion_matrix * ob.matrix_world * axis_conversion_matrix
else:
o3d_matrix = axis_conversion_matrix @ ob.matrix_world
o3d_matrix = axis_conversion_matrix @ ob.matrix_world @ axis_conversion_matrix
o3d_matrix.transpose()
me.transform(ob.matrix_world)
me.transform(axis_conversion_matrix)
if ob.matrix_world.is_negative:
me.flip_normals()

log("Exported matrix: \n{0}".format(o3d_matrix))

bm = bmesh.new()
bm.from_mesh(me)

Expand Down Expand Up @@ -264,7 +272,8 @@ def do_export(filepath, context, global_matrix, use_selection, o3d_version):
# Export the mesh if it hasn't already been exported
if path not in exported_paths:
exported_paths.add(path)
export_mesh(path, context, ob_eval, me, [x for y in o3d_matrix for x in y], ob_eval.material_slots, o3d_version)
export_mesh(path, context, ob_eval, me, [x for y in o3d_matrix for x in y], ob_eval.material_slots,
o3d_version, export_custom_normals)

index += 1

Expand Down
Loading

0 comments on commit 734b4df

Please sign in to comment.