Skip to content

Commit

Permalink
Release - 2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
simon50keda committed Apr 28, 2021
1 parent 92708cf commit 71d35b9
Show file tree
Hide file tree
Showing 50 changed files with 2,191 additions and 779 deletions.
2 changes: 1 addition & 1 deletion addon/io_scs_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"name": "SCS Tools",
"description": "Setup models, Import-Export SCS data format",
"author": "Simon Lusenc (50keda), Milos Zajic (4museman)",
"version": (2, 0, "dd0ff0b"),
"version": (2, 1, "ce5d64bc"),
"blender": (2, 81, 0),
"location": "File > Import-Export",
"wiki_url": "http://modding.scssoft.com/wiki/Documentation/Tools/SCS_Blender_Tools",
Expand Down
2 changes: 2 additions & 0 deletions addon/io_scs_tools/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ class VehicleTypes:
"""Name of the object inside the group which visibility tells us either group should be exported or no."""
model_variant_prop = ".scs_variant"
"""Name of the property for saving variant of the model inside group encapsulating imported paintable model."""
main_coll_name = ".scs_main_collection"
"""Name of the collection where left over objects will be linked to, when importing from sii data."""

id_mask_colors = (
(51, 0, 0),
Expand Down
29 changes: 24 additions & 5 deletions addon/io_scs_tools/exp/pim/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat
missing_skinned_verts = set() # indicates if object is having only partial skin, which is not allowed in our models
has_unnormalized_skin = False # indicates if object has vertices which bones weight sum is smaller then one
last_tangents_uv_layer = None # stores uv layer for which tangents were calculated, so tangents won't be calculated all over again
max_vcolor = 0 # indicates maximum vertex color inside this model and is used to report unnormalized vertex color over 1.0

for poly in mesh.polygons:

Expand Down Expand Up @@ -343,20 +344,35 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat
uvs_aliases.append(aliases)

# 4. vcol -> vcol_lay = mesh.vertex_colors[0].data; vcol_lay[loop_i].color
vcol_multi = mesh_obj.data.scs_props.vertex_color_multiplier
if _MESH_consts.default_vcol not in mesh.vertex_colors: # get RGB component of RGBA
vcol = (1.0,) * 3
missing_vcolor = True
else:
color = mesh.vertex_colors[_MESH_consts.default_vcol].data[loop_i].color
vcol = (color[0] * 2 * vcol_multi, color[1] * 2 * vcol_multi, color[2] * 2 * vcol_multi)
color = list(mesh.vertex_colors[_MESH_consts.default_vcol].data[loop_i].color[:3])

for i in range(0, 3):
# since blender is saving vcolor in 8-bits 0.5 can not be set, thus clamp 128/255 to 0.5 or report to big vcolor otherwise
if 0.5 < color[i] <= 0.501960813999176:
color[i] = 0.5
elif color[i] > 0.501960813999176 and color[i] > max_vcolor:
max_vcolor = color[i]

vcol = (color[0] * 2, color[1] * 2, color[2] * 2)

if _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix not in mesh.vertex_colors: # get A component of RGBA
vcol += (1.0,)
missing_vcolor_a = True
else:
alpha = mesh.vertex_colors[_MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix].data[loop_i].color
vcol += ((alpha[0] + alpha[1] + alpha[2]) / 3.0 * 2 * vcol_multi,) # take avg of colors for alpha
alpha = mesh.vertex_colors[_MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix].data[loop_i].color[:3]
alpha = (alpha[0] + alpha[1] + alpha[2]) / 3.0 # take avg of colors for alpha

# since blender is saving vcolor in 8-bits 0.5 can not be set, thus clamp 128/255 to 0.5 or report to big vcolor otherwise
if 0.5 < alpha <= 0.501960813999176:
alpha = 0.5
elif alpha > 0.501960813999176 and alpha > max_vcolor:
max_vcolor = alpha

vcol += (alpha * 2,)

# 5. tangent -> loop.tangent; loop.bitangent_sign -> calc_tangents() has to be called before
if pim_materials[pim_mat_name].get_nmap_uv_name(): # calculate tangents only if needed
Expand Down Expand Up @@ -466,6 +482,9 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat
lprint("W Object %r from SCS Root %r has unormalized skinning, exporting normalized weights!\n\t "
"You can normalize weights by selecting object & executing 'Normalize All Vertex Groups'.",
(mesh_obj.name, root_object.name))
if max_vcolor != 0:
lprint("W Object %r from SCS Root %r has unormalized vertex color, some vertices use: %.5f. instead of max 0.5",
(mesh_obj.name, root_object.name, max_vcolor))

# add rest of the pieces to global list
for piece_key in mesh_pieces:
Expand Down
7 changes: 3 additions & 4 deletions addon/io_scs_tools/exp/pim_ef/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,20 +260,19 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat

# 4. vcol -> vcol_lay = mesh.vertex_colors[0].data; vcol_lay[loop_i].color
rgbas = []
vcol_multi = mesh_obj.data.scs_props.vertex_color_multiplier
if _MESH_consts.default_vcol not in mesh.vertex_colors: # get RGB component of RGBA
vcol = (1.0,) * 3
missing_vcolor = True
else:
color = mesh.vertex_colors[_MESH_consts.default_vcol].data[loop_i].color
vcol = (color[0] * 2 * vcol_multi, color[1] * 2 * vcol_multi, color[2] * 2 * vcol_multi)
vcol = (color[0] * 2, color[1] * 2, color[2] * 2)

if _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix not in mesh.vertex_colors: # get A component of RGBA
vcol += (1.0,)
missing_vcolor_a = True
else:
alpha = mesh.vertex_colors[_MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix].data[loop_i].color
vcol += ((alpha[0] + alpha[1] + alpha[2]) / 3.0 * 2 * vcol_multi,) # take avg of colors for alpha
vcol += ((alpha[0] + alpha[1] + alpha[2]) / 3.0 * 2,) # take avg of colors for alpha

rgbas.append(vcol)
rgbas_names[_MESH_consts.default_vcol] = True
Expand All @@ -286,7 +285,7 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat
continue

color = vcol_layer.data[loop_i].color
vcol = (color[0] * 2 * vcol_multi, color[1] * 2 * vcol_multi, color[2] * 2 * vcol_multi)
vcol = (color[0] * 2, color[1] * 2, color[2] * 2)

rgbas.append(vcol)
rgbas_names[vcol_layer.name] = True
Expand Down
21 changes: 17 additions & 4 deletions addon/io_scs_tools/exp/pit.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ def get_texture_path_from_material(material, texture_type, export_path):
:type material: bpy.types.Material
:param texture_type: type of texture which should be readed from material (example "texture_base")
:type texture_type: str
:param export_path: path where PIT of this material and texture is gonna be exported
:type export_path: str
:return: relative path for Texture section data of PIT material
:rtype: str
"""
Expand Down Expand Up @@ -160,7 +162,7 @@ def get_texture_path_from_material(material, texture_type, export_path):

# search for relative path inside current scs project base and
# possible dlc/mod parent folders; use first found
for infix in ("", "../base/", "../../base/"):
for infix in ("", "../base/", "../base_vehicle/", "../../base/", "../../base_vehicle/"):

curr_path = os.path.join(scs_project_path, infix + texture_raw_path[2:] + ext)

Expand Down Expand Up @@ -189,12 +191,14 @@ def get_texture_path_from_material(material, texture_type, export_path):
# if we are exporting somewhere into SCS Project Base Path texture still can be saved
if scs_project_path != "" and _path_utils.startswith(export_path, scs_project_path):

tex_dir, tex_filename = os.path.split(texture_raw_path_with_ext)
tex_dir, tex_filename = os.path.split(texture_raw_path)
tobj_filename = tex_filename + ".tobj"

texture_copied_path_with_ext = os.path.join(export_path, tex_filename) + ext

# copy texture beside exported files
try:
shutil.copy2(texture_raw_path_with_ext, os.path.join(export_path, tex_filename))
shutil.copy2(texture_raw_path_with_ext, texture_copied_path_with_ext)
except OSError as e:
# ignore copying the same file
# NOTE: happens if absolute texture paths are used
Expand All @@ -215,6 +219,13 @@ def get_texture_path_from_material(material, texture_type, export_path):
tobj_rel_filepath = tobj_rel_filepath + os.sep + tobj_filename[:-5]
tobj_abs_filepath = os.path.join(export_path, tobj_filename)
texture_abs_filepath = texture_raw_path_with_ext

lprint("W Material %r texture of type %r uses absolute path!\n\t " +
"Texture copied into the Project Base Path beside exported PIT file:\n\t " +
"Original path: %r\n\t " +
"Copied path: %r",
(material.name, texture_type, texture_abs_filepath, texture_copied_path_with_ext))

break

else:
Expand All @@ -225,7 +236,7 @@ def get_texture_path_from_material(material, texture_type, export_path):

else:
lprint("E Texture file %r from material %r doesn't exists inside current Project Base Path.\n\t " +
"TOBJ won't be exported and reference will remain empty, expect problems!",
"TOBJ won't be exported and reference will remain empty, expect problems!",
(texture_raw_path, material.name))
return ""

Expand Down Expand Up @@ -562,6 +573,8 @@ def export(root_object, filepath, name_suffix, used_parts, used_materials):

elif attr_prop == "Tag" and "aux" in attribute_dict[attr_prop]:
attribute_section.props.append((attr_prop, "aux[" + attribute_dict[attr_prop][3:] + "]"))
elif attr_prop == "FriendlyTag":
continue
else:
attribute_section.props.append((attr_prop, attribute_dict[attr_prop]))

Expand Down
38 changes: 21 additions & 17 deletions addon/io_scs_tools/imp/pim.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def get_material_properties(section):
def get_piece_properties(section):
"""Receives a Piece section and returns its properties in its own variables.
For any item that fails to be found, it returns None."""
ob_index = ob_material = ob_vertex_cnt = ob_edge_cnt = ob_face_cnt = ob_stream_cnt = 0
ob_index = ob_material = ob_vertex_cnt = ob_tris_cnt = ob_stream_cnt = 0
for prop in section.props:
if prop[0] in ("", "#"):
pass
Expand All @@ -127,15 +127,13 @@ def get_piece_properties(section):
ob_material = prop[1]
elif prop[0] == "VertexCount":
ob_vertex_cnt = prop[1]
elif prop[0] == "EdgeCount":
ob_edge_cnt = prop[1]
elif prop[0] in ("TriangleCount", "FaceCount"):
ob_face_cnt = prop[1]
elif prop[0] == "TriangleCount":
ob_tris_cnt = prop[1]
elif prop[0] == "StreamCount":
ob_stream_cnt = prop[1]
else:
lprint('\nW Unknown property in "Piece" data: "%s"!', prop[0])
return ob_index, ob_material, ob_vertex_cnt, ob_edge_cnt, ob_face_cnt, ob_stream_cnt
return ob_index, ob_material, ob_vertex_cnt, ob_tris_cnt, ob_stream_cnt


def _get_piece_streams(section):
Expand Down Expand Up @@ -447,18 +445,21 @@ def _create_piece(
else:
mesh_rgb_final = []

vcolor_corrupt = False
for vc_layer_name in mesh_rgb_final:
max_value = mesh_rgb_final[vc_layer_name][0][0] / 2

for vc_entry in mesh_rgb_final[vc_layer_name]:
for i, value in enumerate(vc_entry):
if max_value < value / 2:
max_value = value / 2
# check for vcolor bigger than possible float range (since we divide our vcolor by 2 max value is 2)
max_vcolor = 2.0
for k, vc_entry in enumerate(mesh_rgb_final[vc_layer_name]):
for j, value in enumerate(vc_entry):
if value > max_vcolor:
mesh_rgb_final[vc_layer_name][k][j] = max_vcolor
vcolor_corrupt = True

if max_value > mesh.scs_props.vertex_color_multiplier:
mesh.scs_props.vertex_color_multiplier = max_value
_mesh_utils.bm_make_vc_layer(5, bm, vc_layer_name, mesh_rgb_final[vc_layer_name])

_mesh_utils.bm_make_vc_layer(5, bm, vc_layer_name, mesh_rgb_final[vc_layer_name], mesh.scs_props.vertex_color_multiplier)
if vcolor_corrupt:
lprint("W Piece %r has vertices with vertex color greater the 1.0, clamping it!", (name, ))

context.window_manager.progress_update(0.5)

Expand Down Expand Up @@ -742,9 +743,13 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa
]
elif section.type == 'Piece':
if scs_globals.import_pim_file:
ob_index, ob_material, ob_vertex_cnt, ob_edge_cnt, ob_face_cnt, ob_stream_cnt = get_piece_properties(section)
ob_index, ob_material, ob_vertex_cnt, ob_tris_cnt, ob_stream_cnt = get_piece_properties(section)
piece_name = 'piece_' + str(ob_index)

if ob_vertex_cnt == 0 or ob_tris_cnt == 0:
lprint("W Piece with index %i has no vertices or triangles, ignoring piece import in model:\n\t %r!", (ob_index, filepath))
continue

# print('Piece %i going to "get_piece_5_streams"...' % ob_index)
(mesh_vertices,
mesh_normals,
Expand Down Expand Up @@ -1023,8 +1028,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa
# CREATE SKELETON (ARMATURE)
armature = None
if scs_globals.import_pis_file and bones:
bpy.ops.object.add(type='ARMATURE')
bpy.ops.object.editmode_toggle()
bpy.ops.object.add(type='ARMATURE', enter_editmode=True)
for bone in bones:
bpy.ops.armature.bone_primitive_add(name=bone)
bpy.ops.object.editmode_toggle()
Expand Down
52 changes: 41 additions & 11 deletions addon/io_scs_tools/imp/pim_ef.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#
# ##### END GPL LICENSE BLOCK #####

# Copyright (C) 2017-2019: SCS Software
# Copyright (C) 2017-2020: SCS Software

import bpy
import bmesh
Expand All @@ -26,7 +26,6 @@
from io_scs_tools.imp.pim import get_header
from io_scs_tools.imp.pim import get_global
from io_scs_tools.imp.pim import get_material_properties
from io_scs_tools.imp.pim import get_piece_properties
from io_scs_tools.imp.pim import get_part_properties
from io_scs_tools.imp.pim import get_locator_properties
from io_scs_tools.imp.pim import get_bones_properties
Expand All @@ -42,6 +41,30 @@
from io_scs_tools.utils import get_scs_globals as _get_scs_globals


def _get_piece_properties(section):
"""Receives a Piece section and returns its properties in its own variables.
For any item that fails to be found, it returns None."""
ob_index = ob_material = ob_vertex_cnt = ob_edge_cnt = ob_face_cnt = ob_stream_cnt = 0
for prop in section.props:
if prop[0] in ("", "#"):
pass
elif prop[0] == "Index":
ob_index = prop[1]
elif prop[0] == "Material":
ob_material = prop[1]
elif prop[0] == "VertexCount":
ob_vertex_cnt = prop[1]
elif prop[0] == "EdgeCount":
ob_edge_cnt = prop[1]
elif prop[0] == "FaceCount":
ob_face_cnt = prop[1]
elif prop[0] == "StreamCount":
ob_stream_cnt = prop[1]
else:
lprint('\nW Unknown property in "Piece" data: "%s"!', prop[0])
return ob_index, ob_material, ob_vertex_cnt, ob_edge_cnt, ob_face_cnt, ob_stream_cnt


def _get_piece_streams(section):
"""Receives a Piece (Exchange Format version) section and returns all its data in its own variables.
For any item that fails to be found, it returns None."""
Expand Down Expand Up @@ -263,19 +286,22 @@ def _create_piece(
if mesh_rgb:
mesh_rgb_final.update(mesh_rgb)

vcolor_corrupt = False
for vc_layer_name in mesh_rgb_final:
max_value = mesh_rgb_final[vc_layer_name][0][0][0] / 2

for vc_entry in mesh_rgb_final[vc_layer_name]:
for v_i in vc_entry:
# check for vcolor bigger than possible float range (since we divide our vcolor by 2 max value is 2)
max_vcolor = 2.0
for k, vc_entry in enumerate(mesh_rgb_final[vc_layer_name]):
for j, v_i in enumerate(vc_entry):
for i, value in enumerate(v_i):
if max_value < value / 2:
max_value = value / 2
if value > max_vcolor:
mesh_rgb_final[vc_layer_name][k][j][i] = max_vcolor
vcolor_corrupt = True

if max_value > mesh.scs_props.vertex_color_multiplier:
mesh.scs_props.vertex_color_multiplier = max_value
_mesh_utils.bm_make_vc_layer(7, bm, vc_layer_name, mesh_rgb_final[vc_layer_name])

_mesh_utils.bm_make_vc_layer(7, bm, vc_layer_name, mesh_rgb_final[vc_layer_name], mesh.scs_props.vertex_color_multiplier)
if vcolor_corrupt:
lprint("W Piece %r has vertices with vertex color greater the 1.0, clamping it!", (name,))

bm.to_mesh(mesh)
mesh.update()
Expand Down Expand Up @@ -489,9 +515,13 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa
]
elif section.type == 'Piece':
if scs_globals.import_pim_file:
ob_index, ob_material, ob_vertex_cnt, ob_edge_cnt, ob_face_cnt, ob_stream_cnt = get_piece_properties(section)
ob_index, ob_material, ob_vertex_cnt, ob_edge_cnt, ob_face_cnt, ob_stream_cnt = _get_piece_properties(section)
piece_name = 'piece_' + str(ob_index)

if ob_vertex_cnt == 0 or ob_face_cnt == 0:
lprint("W Piece with index %i has no vertices or faces, ignoring piece import in model:\n\t %r!", (ob_index, filepath))
continue

(mesh_vertices,
mesh_normals,
mesh_tangents,
Expand Down
Loading

0 comments on commit 71d35b9

Please sign in to comment.