From a77ab646a64fc1e1fa0eeeddeca36f72e4d4a0a0 Mon Sep 17 00:00:00 2001 From: wurunDUK Date: Thu, 30 Jul 2020 01:27:17 +0300 Subject: [PATCH] 1.4.3 version --- CHANGELOG.md | 10 +++ README.md | 6 +- io_scene_cdp3d/__init__.py | 37 ++------- io_scene_cdp3d/export_cdp3d.py | 134 +++++++++++++++++---------------- io_scene_cdp3d/import_cdp3d.py | 48 ++++-------- 5 files changed, 104 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c93a0a6..50b806a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ### Changed ### Fixed +## [1.4.3] 2020-07-30 +### Added +- Removed lower top and bottom bounds option +- Carinfo export will now center positions if main object is present +### Fixed +- Removed a crash when no 'floor_level' was present +- Collision box sizes are calculated using main mesh properly for non-symmetrical objects +- Fixed 'remove doubles' option +- Fixed export log creation even with 'export-log' option turned off + ## [1.4.2] 2020-07-06 ### Added - Added carinfo.cca import, carinfo.txt export for managing positions and mesh list diff --git a/README.md b/README.md index 6eb2e26..21c6c28 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Click "Install..." button at the top and select downloaded zip file You can import a model in File->Import->Crashday (.p3d) When importing you can also add up to four texture path's. If provided, the addon will try to load textures from these folders. ### Exporting -"Lower top bound" and "Life bottom bound" are working the same as in makep3d. -Also you can enable export-log which is created in the same folder as the exported file and contains export log as well as meshes list, used in .cca files. +! Object rotation is not applied on export. +You can enable export-log which is created in the same folder as the exported file and contains export log as well as meshes list, used in .cca files. ##### Lights When exporting you have options to turn on coronas, flares, and environment light up for all the lights. Though, makep3d sets coronas to off and environment light up to on for every light, which may mean whose values are unused. @@ -38,7 +38,7 @@ CD .p3d files do not store any information about normals which means we can not ## TODO: - when importing sort car parts into collections -- maybe load .cca and add positions +- ~~maybe load .cca and add positions~~ - correctly center meshes? - ~~automaticaly set floor~~ - save export import settings if possible diff --git a/io_scene_cdp3d/__init__.py b/io_scene_cdp3d/__init__.py index 04d6d3d..535b2bf 100644 --- a/io_scene_cdp3d/__init__.py +++ b/io_scene_cdp3d/__init__.py @@ -1,27 +1,9 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - bl_info = { "name": "Crashday p3d format", "author": "Wurunduk", "blender": (2, 83, 0), "location": "File > Import-Export", - "version": (1, 4, 2), + "version": (1, 4, 3), "support": 'COMMUNITY', "category": "Import-Export"} @@ -69,10 +51,15 @@ class ImportCDP3D(bpy.types.Operator, ImportHelper): filter_glob: StringProperty(default="*.p3d", options={'HIDDEN'}) use_edge_split_modifier: BoolProperty( - name="Use EdgeSplit modifier for hard edges, remove doubles", + name="Use EdgeSplit, remove doubles", default=True, ) + remove_doubles_distance: FloatProperty( + name="Remove doubles distance", + default=0.00001, + ) + cd_path: StringProperty( name="Path to general texture folder", description="If provided plugin will auto load textures from this folder" @@ -164,16 +151,6 @@ class ExportCDP3D(bpy.types.Operator, ExportHelper): default=True, ) - lower_top_bound: FloatProperty( - name = "Lower Top Bound", - default = 0.0, - ) - - lift_bottom_bound: FloatProperty( - name = "Lift Bottom Bound", - default = 0.0, - ) - export_log: BoolProperty( name = "Export Log", description = "Create a log file of export process with useful data", diff --git a/io_scene_cdp3d/export_cdp3d.py b/io_scene_cdp3d/export_cdp3d.py index 5f0f97a..df7860e 100644 --- a/io_scene_cdp3d/export_cdp3d.py +++ b/io_scene_cdp3d/export_cdp3d.py @@ -1,21 +1,3 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - import struct import datetime import mathutils @@ -98,16 +80,17 @@ def save(operator, enable_flares=True, enable_environment=True, use_empty_for_floor_level=True, - lower_top_bound=0.0, - lift_bottom_bound=0.0, export_log=True): # get the folder where file will be saved and add a log in that folder work_path = '\\'.join(filepath.split('\\')[0:-1]) - log_file = open(work_path + '//export-log.txt', 'a') + log_file = None + if export_log: + log_file = open(work_path + '//export-log.txt', 'a') date = datetime.datetime.now() - log_file.write('Started exporting on {}\nFile path: {}\n'.format(date.strftime('%d-%m-%Y %H:%M:%S'), filepath)) + if log_file: + log_file.write('Started exporting on {}\nFile path: {}\n'.format(date.strftime('%d-%m-%Y %H:%M:%S'), filepath)) print('\nExporting file to {}'.format(filepath)) # create empty p3d model @@ -161,16 +144,19 @@ def save(operator, if main is None: bpy.context.window_manager.popup_menu(error_no_main, title='No main mesh', icon='ERROR') print('!!! Failed to export p3d. No main mesh found.') - log_file.write('!!! Failed to export p3d. No main mesh found.\n') - log_file.close() + if log_file: + log_file.write('!!! Failed to export p3d. No main mesh found.\n') + log_file.close() return {'CANCELLED'} if shad is None: print('! Shadow mesh was not found, using main mesh for shadow.') - log_file.write('! Shadow mesh was not found, using main mesh for shadow.\n') + if log_file: + log_file.write('! Shadow mesh was not found, using main mesh for shadow.\n') if coll is None: print('! Collision mesh was not found, using main mesh for collisions.') - log_file.write('! Collision mesh was not found, using main mesh for collisions.\n') + if log_file: + log_file.write('! Collision mesh was not found, using main mesh for collisions.\n') # the main mesh in p3d is always at 0.0. # this means we need to move all other models alongside main mesh @@ -238,15 +224,21 @@ def save(operator, # save model bounds if ob == main: - floor_level = bpy.data.objects['floor_level'] + floor_level = bpy.data.objects.get('floor_level') if floor_level is not None and use_empty_for_floor_level: - m.height = -floor_level.location[2]*2 + fl_pos = floor_level.location + m.height = -(fl_pos - main.location)[2]*2 + + # this is size caluclation which is done in original p3d + # but this breaks collision for non-symmetrical tiles + #p.length = m.length + #p.height = m.height + #p.depth = m.depth - m.height -= lower_top_bound + lift_bottom_bound - p.length = m.length p.height = m.height - p.depth = m.depth + p.length = max(highx, -lowx) * 2 + p.depth = max(highy, -lowy) * 2 # while this looks dumb, this is how original makep3d works if p.length >= 19.95 and p.length <= 20.05: p.length = 20 @@ -348,7 +340,8 @@ def save(operator, if len(m.vertices) == 0 or len(m.polys) == 0: print('Can\'t export empty mesh "{}". Ignoring'.format(m.name)) - log_file.write('Can\'t export empty mesh "{}". Ignoring'.format(m.name)) + if log_file: + log_file.write('Can\'t export empty mesh "{}". Ignoring'.format(m.name)) else: p.num_meshes += 1 p.meshes.append(m) @@ -361,28 +354,28 @@ def save(operator, file.close() print('p3d exported') - log_file.write('Meshes: {}\n'.format(exported_meshes_string)) - log_file.write('Finished p3d export.\n\n') - log_file.close() + if log_file: + log_file.write('Meshes: {}\n'.format(exported_meshes_string)) + log_file.write('Finished p3d export.\n\n') + log_file.close() return {'FINISHED'} -def save_pos(f, col, name): +def save_pos(f, col, offset, name): obj = col.objects.get(name) pos = (0.0,0.0,0.0) if obj is not None: p = obj.location - pos = (p[0], p[2], p[1]) + pos = (p[0] - offset[0], p[2] - offset[2], p[1] - offset[1]) f.write('{:.4g} {:.4g} {:.4g} \t\t\t # {}{}\n'.format(pos[0], pos[1], pos[2], '!!!NOT FOUND ON EXPORT ' if obj is None else '', name)) -def save_pos2(f, col, name): +def save_pos2(f, col, offset, name): obj = col.objects.get(name) - print(obj) pos = (0.0,0.0,0.0) if obj is not None: p = obj.location - pos = (p[0], p[2], p[1]) + pos = (p[0] - offset[0], p[2] - offset[2], p[1] - offset[1]) f.write('{:.4g} \t\t\t # {}{}\n'.format(pos[2], '!!!NOT FOUND ON EXPORT ' if obj is None else '', name)) @@ -394,38 +387,51 @@ def save_cca(operator, exported_meshes_string = '' + # store main, shadow and collision meshes + main = None + mpos = (0.0,0.0,0.0) + + # find the main mesh of the model for ob in col.collection.all_objects: if ob.type == 'MESH': exported_meshes_string += ob.name + ' ' + if ob.name == 'main': + main = ob + mpos = main.location + + # p3d models must have a main mesh + if main is None: + print('!!! No main mesh found, .cca values might be wrong if main is not centered.') + f.write('!!! No main mesh found, .cca values might be wrong if main is not centered.') f.write('Meshes: {}\n\n'.format(exported_meshes_string)) - save_pos(f, col, 'center_of_gravity_pos') + save_pos(f, col, mpos, 'center_of_gravity_pos') f.write('\n') - save_pos(f, col, 'left_upper_wheel_pos') - save_pos(f, col, 'right_lower_wheel_pos') - save_pos(f, col, 'minigun_pos') + save_pos(f, col, mpos, 'left_upper_wheel_pos') + save_pos(f, col, mpos, 'right_lower_wheel_pos') + save_pos(f, col, mpos, 'minigun_pos') f.write('0.0\t\t\t # Angle of minigun (negative values for downpointing)\n') - save_pos(f, col, 'mines_pos') - save_pos(f, col, 'missiles_pos') - save_pos(f, col, 'driver_pos') - save_pos(f, col, 'exhaust_pos') - save_pos(f, col, 'exhaust2_pos') - save_pos(f, col, 'flag_pos') - save_pos(f, col, 'bomb_pos') - save_pos(f, col, 'cockpit_cam_pos') - save_pos(f, col, 'roof_cam_pos') - save_pos(f, col, 'hood_cam_pos') - save_pos(f, col, 'bumper_cam_pos') - save_pos(f, col, 'rear_view_cam_pos') - save_pos(f, col, 'left_side_cam_pos') - save_pos(f, col, 'right_side_cam_pos') - save_pos(f, col, 'driver1_cam_pos') - save_pos(f, col, 'driver2_cam_pos') - save_pos(f, col, 'driver3_cam_pos') - save_pos(f, col, 'steering_wheel_pos') - save_pos(f, col, 'car_cover_pos') - save_pos2(f, col, 'engine_pos') + save_pos(f, col, mpos, 'mines_pos') + save_pos(f, col, mpos, 'missiles_pos') + save_pos(f, col, mpos, 'driver_pos') + save_pos(f, col, mpos, 'exhaust_pos') + save_pos(f, col, mpos, 'exhaust2_pos') + save_pos(f, col, mpos, 'flag_pos') + save_pos(f, col, mpos, 'bomb_pos') + save_pos(f, col, mpos, 'cockpit_cam_pos') + save_pos(f, col, mpos, 'roof_cam_pos') + save_pos(f, col, mpos, 'hood_cam_pos') + save_pos(f, col, mpos, 'bumper_cam_pos') + save_pos(f, col, mpos, 'rear_view_cam_pos') + save_pos(f, col, mpos, 'left_side_cam_pos') + save_pos(f, col, mpos, 'right_side_cam_pos') + save_pos(f, col, mpos, 'driver1_cam_pos') + save_pos(f, col, mpos, 'driver2_cam_pos') + save_pos(f, col, mpos, 'driver3_cam_pos') + save_pos(f, col, mpos, 'steering_wheel_pos') + save_pos(f, col, mpos, 'car_cover_pos') + save_pos2(f, col, mpos, 'engine_pos') f.close() return {'FINISHED'} \ No newline at end of file diff --git a/io_scene_cdp3d/import_cdp3d.py b/io_scene_cdp3d/import_cdp3d.py index b605b21..3e6ece1 100644 --- a/io_scene_cdp3d/import_cdp3d.py +++ b/io_scene_cdp3d/import_cdp3d.py @@ -1,21 +1,3 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - import os import time import struct @@ -29,8 +11,6 @@ import bmesh from . import p3d -use_edge_split = True - def texture_exists(full_path, file_name): #remove extension and add to path p = os.path.join(full_path, os.path.splitext(file_name)[0]) @@ -118,7 +98,7 @@ def add_material(obj, material_name): obj.data.materials.append(material) -def create_meshes(p3d_model, col): +def create_meshes(p3d_model, col, use_edge_split_modifier, remove_doubles_distance): for m in p3d_model.meshes: mesh = bpy.data.meshes.new(name=m.name) obj = bpy.data.objects.new(mesh.name, mesh) @@ -154,18 +134,19 @@ def create_meshes(p3d_model, col): uv_layer.data.foreach_set('uv', [uv for pair in [uvs[i] for i, l in enumerate(mesh.loops)] for uv in pair]) - bm = bmesh.new() - bm.from_mesh(mesh) + if use_edge_split_modifier: + bm = bmesh.new() + bm.from_mesh(mesh) - for edge in bm.edges: - if len(edge.link_loops) == 1: - edge.smooth = False + for edge in bm.edges: + if len(edge.link_loops) == 1: + edge.smooth = False - bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) - mod = obj.modifiers.new("EdgeSplit", 'EDGE_SPLIT') - mod.use_edge_angle = False - bm.to_mesh(mesh) - bm.free() + bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=remove_doubles_distance) + mod = obj.modifiers.new("EdgeSplit", 'EDGE_SPLIT') + mod.use_edge_angle = False + bm.to_mesh(mesh) + bm.free() def create_lights(p3d_model, col): @@ -189,6 +170,7 @@ def create_pos(col, pos, name): def load(operator, context, use_edge_split_modifier=True, + remove_doubles_distance=0.00001, filepath='', cd_path='', car_path='', cd_path_mod='', car_path_mod=''): @@ -197,8 +179,6 @@ def load(operator, print('\nImporting file {} from {}'.format(file_name, filepath)) - use_edge_split = use_edge_split_modifier - search_path = [] if os.path.exists(cd_path): search_path.append(cd_path) @@ -222,7 +202,7 @@ def load(operator, add_textures(p, search_path) create_lights(p, col) - create_meshes(p, col) + create_meshes(p, col, use_edge_split_modifier, remove_doubles_distance) create_pos(col, (0.0, 0.0, - p.height/2.0), 'floor_level')