diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 6f8e2e589..2715d5b66 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -38,8 +38,10 @@ VGRADIENT = 0x1300 # The background gradient colors USE_VGRADIENT = 0x1301 # The background gradient flag O_CONSTS = 0x1500 # The origin of the 3D cursor AMBIENTLIGHT = 0x2100 # The color of the ambient light -LAYER_FOG = 0x2302 # The fog atmosphere settings -USE_LAYER_FOG = 0x2303 # The fog atmosphere flag +FOG = 0x2200 # The fog atmosphere settings +USE_FOG = 0x2201 # The fog atmosphere flag +LAYER_FOG = 0x2302 # The fog layer atmosphere settings +USE_LAYER_FOG = 0x2303 # The fog layer atmosphere flag MATERIAL = 45055 # 0xAFFF // This stored the texture info OBJECT = 16384 # 0x4000 // This stores the faces, vertices, etc... @@ -1468,7 +1470,7 @@ def make_ambient_node(world): emission = next((lk.from_socket.node for lk in ambilinks if lk.to_node.type in ambioutput), False) ambinode = next((lk.from_socket.node for lk in ambilinks if lk.to_node.type == 'EMISSION'), emission) kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points] - ambipath = ('nodes[\"RGB\"].outputs[0].default_value' if ambinode.type == 'RGB' else + ambipath = ('nodes[\"RGB\"].outputs[0].default_value' if ambinode and ambinode.type == 'RGB' else 'nodes[\"Emission\"].inputs[0].default_value') nkeys = len(kframes) if not 0 in kframes: @@ -1615,16 +1617,18 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, # Add BACKGROUND and BITMAP if world.use_nodes: + bgtype = 'BACKGROUND' ntree = world.node_tree.links background_color_chunk = _3ds_chunk(RGB) background_chunk = _3ds_chunk(SOLIDBACKGND) background_flag = _3ds_chunk(USE_SOLIDBGND) - bgtype = 'BACKGROUND' + bgmixer = 'BACKGROUND', 'MIX', 'MIX_RGB' bgshade = 'ADD_SHADER', 'MIX_SHADER', 'OUTPUT_WORLD' bg_tex = 'TEX_IMAGE', 'TEX_ENVIRONMENT' bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.from_node.type == bgtype and lk.to_node.type in bgshade), world.color) - bg_mixer = next((lk.from_node.type for lk in ntree if lk.from_node.type in {'MIX', 'MIX_RGB'} and lk.to_node.type == bgtype), bgtype) + bg_mixer = next((lk.from_node.type for lk in ntree if lk.from_node.type in bgmixer and lk.to_node.type == bgtype), bgtype) bg_image = next((lk.from_node.image.name for lk in ntree if lk.from_node.type in bg_tex and lk.to_node.type == bg_mixer), False) + gradient = next((lk.from_node.color_ramp.elements for lk in ntree if lk.from_node.type == 'VALTORGB' and lk.to_node.type in bgmixer), False) background_color_chunk.add_variable("color", _3ds_float_color(bg_color)) background_chunk.add_subchunk(background_color_chunk) if bg_image: @@ -1633,28 +1637,59 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, background_image.add_variable("image", _3ds_string(sane_name(bg_image))) object_info.add_subchunk(background_image) object_info.add_subchunk(background_chunk) + + # Add VGRADIENT chunk + if gradient and len(gradient) >= 3: + gradient_chunk = _3ds_chunk(VGRADIENT) + background_flag = _3ds_chunk(USE_VGRADIENT) + gradient_chunk.add_variable("midpoint", _3ds_float(gradient[1].position)) + gradient_topcolor_chunk = _3ds_chunk(RGB) + gradient_topcolor_chunk.add_variable("color", _3ds_float_color(gradient[2].color[:3])) + gradient_chunk.add_subchunk(gradient_topcolor_chunk) + gradient_midcolor_chunk = _3ds_chunk(RGB) + gradient_midcolor_chunk.add_variable("color", _3ds_float_color(gradient[1].color[:3])) + gradient_chunk.add_subchunk(gradient_midcolor_chunk) + gradient_lowcolor_chunk = _3ds_chunk(RGB) + gradient_lowcolor_chunk.add_variable("color", _3ds_float_color(gradient[0].color[:3])) + gradient_chunk.add_subchunk(gradient_lowcolor_chunk) + object_info.add_subchunk(gradient_chunk) object_info.add_subchunk(background_flag) - # Add LAYER_FOG settings - fogshader = next((lk.from_socket.node for lk in ntree if lk.from_socket.identifier and lk.to_socket.identifier == 'Volume'), False) - if fogshader: - fogflag = 0 - if world.mist_settings.falloff == 'QUADRATIC': - fogflag |= 0x1 - if world.mist_settings.falloff == 'INVERSE_QUADRATIC': - fogflag |= 0x2 - fog_chunk = _3ds_chunk(LAYER_FOG) + # Add FOG + fognode = next((lk.from_socket.node for lk in ntree if lk.from_socket.node.type == 'VOLUME_ABSORPTION' and lk.to_socket.node.type in bgshade), False) + if fognode: + fog_chunk = _3ds_chunk(FOG) fog_color_chunk = _3ds_chunk(RGB) - use_fog_flag = _3ds_chunk(USE_LAYER_FOG) - fog_color_chunk.add_variable("color", _3ds_float_color(fogshader.inputs['Color'].default_value[:3])) - fog_chunk.add_variable("lowZ", _3ds_float(world.mist_settings.start)) - fog_chunk.add_variable("highZ", _3ds_float(world.mist_settings.depth)) - fog_chunk.add_variable("density", _3ds_float(fogshader.inputs['Density'].default_value)) - fog_chunk.add_variable("flags", _3ds_uint(fogflag)) + use_fog_flag = _3ds_chunk(USE_FOG) + fog_density = fognode.inputs['Density'].default_value * 100 + fog_color_chunk.add_variable("color", _3ds_float_color(fognode.inputs[0].default_value[:3])) + fog_chunk.add_variable("nearplane", _3ds_float(world.mist_settings.start)) + fog_chunk.add_variable("nearfog", _3ds_float(fog_density * 0.5)) + fog_chunk.add_variable("farplane", _3ds_float(world.mist_settings.depth)) + fog_chunk.add_variable("farfog", _3ds_float(fog_density + fog_density * 0.5)) fog_chunk.add_subchunk(fog_color_chunk) object_info.add_subchunk(fog_chunk) - if layer.use_pass_mist: - object_info.add_subchunk(use_fog_flag) + + # Add LAYER FOG + foglayer = next((lk.from_socket.node for lk in ntree if lk.from_socket.node.type == 'VOLUME_SCATTER' and lk.to_socket.node.type in bgshade), False) + if foglayer: + layerfog_flag = 0 + if world.mist_settings.falloff == 'QUADRATIC': + layerfog_flag |= 0x1 + if world.mist_settings.falloff == 'INVERSE_QUADRATIC': + layerfog_flag |= 0x2 + layerfog_chunk = _3ds_chunk(LAYER_FOG) + layerfog_color_chunk = _3ds_chunk(RGB) + use_fog_flag = _3ds_chunk(USE_LAYER_FOG) + layerfog_color_chunk.add_variable("color", _3ds_float_color(foglayer.inputs[0].default_value[:3])) + layerfog_chunk.add_variable("lowZ", _3ds_float(world.mist_settings.start)) + layerfog_chunk.add_variable("highZ", _3ds_float(world.mist_settings.height)) + layerfog_chunk.add_variable("density", _3ds_float(foglayer.inputs[1].default_value)) + layerfog_chunk.add_variable("flags", _3ds_uint(layerfog_flag)) + layerfog_chunk.add_subchunk(layerfog_color_chunk) + object_info.add_subchunk(layerfog_chunk) + if fognode or foglayer and layer.use_pass_mist: + object_info.add_subchunk(use_fog_flag) if use_keyframes and world.animation_data: kfdata.add_subchunk(make_ambient_node(world)) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 850396254..42a49bc7f 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -45,8 +45,11 @@ VGRADIENT = 0x1300 # The background gradient colors USE_VGRADIENT = 0x1301 # The background gradient flag O_CONSTS = 0x1500 # The origin of the 3D cursor AMBIENTLIGHT = 0x2100 # The color of the ambient light -LAYER_FOG = 0x2302 # The fog atmosphere settings -USE_LAYER_FOG = 0x2303 # The fog atmosphere flag +FOG = 0x2200 # The fog atmosphere settings +USE_FOG = 0x2201 # The fog atmosphere flag +FOG_BGND = 0x2210 # The fog atmosphere background flag +LAYER_FOG = 0x2302 # The fog layer atmosphere settings +USE_LAYER_FOG = 0x2303 # The fog layer atmosphere flag MATERIAL = 0xAFFF # This stored the texture info OBJECT = 0x4000 # This stores the faces, vertices, etc... @@ -746,16 +749,95 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') bitmap_mix.label = "Solid Color" bitmapnode.label = "Bitmap: " + bitmap_name - bitmap_mix.location = (-250, 360) - bitmapnode.location = (-600, 300) bitmap_mix.inputs[2].default_value = nodes['Background'].inputs[0].default_value bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True) - bitmap_mix.inputs[0].default_value = 0.0 if bitmapnode.image is not None else 1.0 + bitmap_mix.inputs[0].default_value = 0.5 if bitmapnode.image is not None else 1.0 + bitmapnode.location = (-600, 360) if bitmapnode.image is not None else (-600, 300) + bitmap_mix.location = (-250, 300) links.new(bitmap_mix.outputs['Color'], nodes['Background'].inputs[0]) links.new(bitmapnode.outputs['Color'], bitmap_mix.inputs[1]) new_chunk.bytes_read += read_str_len - # If fog chunk + # If gradient chunk: + elif CreateWorld and new_chunk.ID == VGRADIENT: + if contextWorld is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + contextWorld = bpy.data.worlds.new("Gradient: " + realname) + context.scene.world = contextWorld + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + gradientnode = nodes.new(type='ShaderNodeValToRGB') + gradientnode.location = (-600, 100) + gradientnode.label = "Gradient" + backgroundmix = next((wn for wn in worldnodes if wn.type in {'MIX', 'MIX_RGB'}), False) + if backgroundmix: + links.new(gradientnode.outputs['Color'], backgroundmix.inputs[2]) + else: + links.new(gradientnode.outputs['Color'], nodes['Background'].inputs[0]) + gradientnode.color_ramp.elements.new(read_float(new_chunk)) + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + gradientnode.color_ramp.elements[2].color[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + gradientnode.color_ramp.elements[2].color[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + gradientnode.color_ramp.elements[1].color[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + gradientnode.color_ramp.elements[1].color[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + gradientnode.color_ramp.elements[0].color[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + gradientnode.color_ramp.elements[0].color[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + + # If fog chunk: + elif CreateWorld and new_chunk.ID == FOG: + if contextWorld is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + newWorld = bpy.data.worlds.new("LayerFog: " + realname) + context.scene.world = contextWorld + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + fognode = nodes.new(type='ShaderNodeVolumeAbsorption') + fognode.label = "Fog" + fognode.location = (300, 60) + volumemix = next((wn for wn in worldnodes if wn.label == 'Volume' and wn.type in {'ADD_SHADER', 'MIX_SHADER'}), False) + if volumemix: + links.new(fognode.outputs['Volume'], volumemix.inputs[1]) + else: + links.new(fognode.outputs[0], nodes['World Output'].inputs[1]) + contextWorld.mist_settings.use_mist = True + contextWorld.mist_settings.start = read_float(new_chunk) + nearfog = read_float(new_chunk) * 0.01 + contextWorld.mist_settings.depth = read_float(new_chunk) + farfog = read_float(new_chunk) * 0.01 + fognode.inputs[1].default_value = (nearfog + farfog) * 0.5 + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + fognode.inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + fognode.inputs[0].default_value[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + elif CreateWorld and new_chunk.ID == FOG_BGND: + pass + + # If layer fog chunk: elif CreateWorld and new_chunk.ID == LAYER_FOG: """Fog options flags are bit 20 (0x100000) for background fogging, bit 0 (0x1) for bottom falloff, and bit 1 (0x2) for top falloff.""" @@ -767,16 +849,23 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextWorld.use_nodes = True links = contextWorld.node_tree.links nodes = contextWorld.node_tree.nodes + mxvolume = nodes.new(type='ShaderNodeMixShader') layerfog = nodes.new(type='ShaderNodeVolumeScatter') layerfog.label = "Layer Fog" - layerfog.location = (300, 100) - links.new(layerfog.outputs['Volume'], nodes['World Output'].inputs['Volume']) + mxvolume.label = "Volume" + layerfog.location = (10, -60) + mxvolume.location = (300, 50) + links.new(layerfog.outputs['Volume'], mxvolume.inputs[2]) + links.new(mxvolume.outputs[0], nodes['World Output'].inputs[1]) + fognode = next((wn for wn in worldnodes if wn.type == 'VOLUME_ABSORPTION'), False) + if fognode: + links.new(fognode.outputs['Volume'], mxvolume.inputs[1]) + fognode.location = (10, 60) context.view_layer.use_pass_mist = False contextWorld.mist_settings.use_mist = True contextWorld.mist_settings.start = read_float(new_chunk) - contextWorld.mist_settings.depth = read_float(new_chunk) - contextWorld.mist_settings.height = contextWorld.mist_settings.depth * 0.5 - layerfog.inputs['Density'].default_value = read_float(new_chunk) + contextWorld.mist_settings.height = read_float(new_chunk) + layerfog.inputs[1].default_value = read_float(new_chunk) layerfog_flag = read_long(new_chunk) if layerfog_flag == 0: contextWorld.mist_settings.falloff = 'LINEAR' @@ -1152,11 +1241,12 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI ambinode = nodes.new(type='ShaderNodeEmission') ambilite = nodes.new(type='ShaderNodeRGB') ambilite.label = "Ambient Color" + mixshade.label = "Surface" ambinode.inputs[0].default_value[:3] = child.color - ambinode.location = (10, 150) - worldout.location = (600, 200) - mixshade.location = (300, 300) - ambilite.location = (-250, 150) + ambinode.location = (10, 180) + worldout.location = (600, 180) + mixshade.location = (300, 280) + ambilite.location = (-250, 100) links.new(mixshade.outputs[0], worldout.inputs['Surface']) links.new(nodes['Background'].outputs[0], mixshade.inputs[1]) links.new(ambinode.outputs[0], mixshade.inputs[2])