Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-04-22 03:24:09 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-04-22 03:24:09 +0400
commit4dd5ac39b82cec6302d4fd699ef13ceb5ccceb53 (patch)
tree13fcebf1a61afbdab8882b909b9f801970d1630c /io_scene_3ds
parent600104361be44393d36dadd3040a5326303e0ff1 (diff)
patch: [#30986] 3DS importer/exporter material improvements and bug fixes.
from Alexander Gessler (aramis_acg) This patch extends the 3DS importer and exporter to have better material support and unifies their respective feature sets. This includes: - UV scale,offset - tiling/wrap modes - improved conversion of mapping targets - better warnings Additionally, the exporter now handles specular, opacity and normal maps correctly (the importer did this already). (I think the way how textures were written previously was non 3DS-compliant). I tested this against assimp's 3DS test suite [1] as well as my personal 3DS test sets. Exported 3DS files were successfully read by FBXConv and assimp. [1] http://assimp.svn.sourceforge.net/viewvc/assimp/trunk/test/
Diffstat (limited to 'io_scene_3ds')
-rw-r--r--io_scene_3ds/export_3ds.py129
-rw-r--r--io_scene_3ds/import_3ds.py70
2 files changed, 173 insertions, 26 deletions
diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py
index 7b37db23..40fdeda3 100644
--- a/io_scene_3ds/export_3ds.py
+++ b/io_scene_3ds/export_3ds.py
@@ -49,8 +49,21 @@ MATAMBIENT = 0xA010 # Ambient color of the object/material
MATDIFFUSE = 0xA020 # This holds the color of the object/material
MATSPECULAR = 0xA030 # SPecular color of the object/material
MATSHINESS = 0xA040 # ??
-MATMAP = 0xA200 # This is a header for a new material
-MATMAPFILE = 0xA300 # This holds the file name of the texture
+
+MAT_DIFFUSEMAP = 0xA200 # This is a header for a new diffuse texture
+MAT_OPACMAP = 0xA210 # head for opacity map
+MAT_BUMPMAP = 0xA230 # read for normal map
+MAT_SPECMAP = 0xA204 # read for specularity map
+
+#>------ sub defines of MAT_???MAP
+MATMAPFILE = 0xA300 # This holds the file name of a texture
+
+MAT_MAP_TILING = 0xa351 # 2nd bit (from LSB) is mirror UV flag
+MAT_MAP_USCALE = 0xA354 # U axis scaling
+MAT_MAP_VSCALE = 0xA356 # V axis scaling
+MAT_MAP_UOFFSET = 0xA358 # U axis offset
+MAT_MAP_VOFFSET = 0xA35A # V axis offset
+MAT_MAP_ANG = 0xA35C # UV rotation around the z-axis in rad
RGB1 = 0x0011
RGB2 = 0x0012
@@ -423,10 +436,10 @@ class _3ds_chunk(object):
# EXPORT
######################################################
-def get_material_images(material):
+def get_material_image_texslots(material):
# blender utility func.
if material:
- return [s.texture.image for s in material.texture_slots if s and s.texture.type == 'IMAGE' and s.texture.image]
+ return [s for s in material.texture_slots if s and s.texture.type == 'IMAGE' and s.texture.image]
return []
# images = []
@@ -454,22 +467,76 @@ def make_material_subchunk(chunk_id, color):
return mat_sub
-def make_material_texture_chunk(chunk_id, images):
- """Make Material Map texture chunk
+def make_material_texture_chunk(chunk_id, texslots, tess_uv_image=None):
+ """Make Material Map texture chunk given a seq. of `MaterialTextureSlot`'s
+
+ `tess_uv_image` is optionally used as image source if the slots are
+ empty. No additional filtering for mapping modes is done, all
+ slots are written "as is".
"""
+
mat_sub = _3ds_chunk(chunk_id)
+ has_entry = False
+
+ import bpy
+
+ def add_texslot(texslot):
+ texture = texslot.texture
+ image = texture.image
- def add_image(image):
- import bpy
filename = bpy.path.basename(image.filepath)
mat_sub_file = _3ds_chunk(MATMAPFILE)
mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename)))
mat_sub.add_subchunk(mat_sub_file)
- for image in images:
- add_image(image)
+ maptile = 0
+
+ # no perfect mapping for mirror modes - 3DS only has uniform mirror w. repeat=2
+ if texture.extension == 'REPEAT' and (texture.use_mirror_x and texture.repeat_x > 1) \
+ or (texture.use_mirror_y and texture.repeat_y > 1):
+ maptile |= 0x2
+ # CLIP maps to 3DS' decal flag
+ elif texture.extension == 'CLIP':
+ maptile |= 0x10
+
+ mat_sub_tile = _3ds_chunk(MAT_MAP_TILING)
+ mat_sub_tile.add_variable("maptiling", _3ds_ushort(maptile))
+ mat_sub.add_subchunk(mat_sub_tile)
+
+ mat_sub_uscale = _3ds_chunk(MAT_MAP_USCALE)
+ mat_sub_uscale.add_variable("mapuscale", _3ds_float(texslot.scale[0]))
+ mat_sub.add_subchunk(mat_sub_uscale)
+
+ mat_sub_vscale = _3ds_chunk(MAT_MAP_VSCALE)
+ mat_sub_vscale.add_variable("mapuscale", _3ds_float(texslot.scale[1]))
+ mat_sub.add_subchunk(mat_sub_vscale)
+
+ mat_sub_uoffset = _3ds_chunk(MAT_MAP_UOFFSET)
+ mat_sub_uoffset.add_variable("mapuoffset", _3ds_float(texslot.offset[0]))
+ mat_sub.add_subchunk(mat_sub_uoffset)
+
+ mat_sub_voffset = _3ds_chunk(MAT_MAP_VOFFSET)
+ mat_sub_voffset.add_variable("mapvoffset", _3ds_float(texslot.offset[1]))
+ mat_sub.add_subchunk(mat_sub_voffset)
+
+ # store all textures for this mapto in order. This at least is what
+ # the 3DS exporter did so far, afaik most readers will just skip
+ # over 2nd textures.
+ for slot in texslots:
+ add_texslot(slot)
+ has_entry = True
+
+ # image from tess. UV face - basically the code above should handle
+ # this already. No idea why its here so keep it :-)
+ if tess_uv_image and not has_entry:
+ has_entry = True
+
+ filename = bpy.path.basename(tess_uv_image.filepath)
+ mat_sub_file = _3ds_chunk(MATMAPFILE)
+ mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename)))
+ mat_sub.add_subchunk(mat_sub_file)
- return mat_sub
+ return mat_sub if has_entry else None
def make_material_chunk(material, image):
@@ -495,12 +562,40 @@ def make_material_chunk(material, image):
material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.diffuse_color[:]))
material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color[:]))
- images = get_material_images(material) # can be None
- if image:
- images.append(image)
-
- if images:
- material_chunk.add_subchunk(make_material_texture_chunk(MATMAP, images))
+ slots = get_material_image_texslots(material) # can be None
+
+ if slots:
+
+ spec = [s for s in slots if s.use_map_specular or s.use_map_color_spec]
+ matmap = make_material_texture_chunk(MAT_SPECMAP, spec)
+ if matmap:
+ material_chunk.add_subchunk(matmap)
+
+ alpha = [s for s in slots if s.use_map_alpha]
+ matmap = make_material_texture_chunk(MAT_OPACMAP, alpha)
+ if matmap:
+ material_chunk.add_subchunk(matmap)
+
+ normal = [s for s in slots if s.use_map_normal]
+ matmap = make_material_texture_chunk(MAT_BUMPMAP, normal)
+ if matmap:
+ material_chunk.add_subchunk(matmap)
+
+ # make sure no textures are lost. Everything that doesn't fit
+ # into a channel is exported as diffuse texture with a
+ # warning.
+ diffuse = []
+ for s in slots:
+ if s.use_map_color_diffuse:
+ diffuse.append(s)
+ elif not (s in normal or s in alpha or s in spec):
+ print('\nwarning: failed to map texture to 3DS map channel, assuming diffuse')
+ diffuse.append(s)
+
+ if diffuse:
+ matmap = make_material_texture_chunk(MAT_DIFFUSEMAP, diffuse, image)
+ if matmap:
+ material_chunk.add_subchunk(matmap)
return material_chunk
diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py
index 32a41b1a..338bf02a 100644
--- a/io_scene_3ds/import_3ds.py
+++ b/io_scene_3ds/import_3ds.py
@@ -66,6 +66,13 @@ MAT_REFLECTION_MAP = 0xA220 # This is a header for a new reflection map
MAT_BUMP_MAP = 0xA230 # This is a header for a new bump map
MAT_MAP_FILEPATH = 0xA300 # This holds the file name of the texture
+MAT_MAP_TILING = 0xa351 # 2nd bit (from LSB) is mirror UV flag
+MAT_MAP_USCALE = 0xA354 # U axis scaling
+MAT_MAP_VSCALE = 0xA356 # V axis scaling
+MAT_MAP_UOFFSET = 0xA358 # U axis offset
+MAT_MAP_VOFFSET = 0xA35A # V axis offset
+MAT_MAP_ANG = 0xA35C # UV rotation around the z-axis in rad
+
MAT_FLOAT_COLOR = 0x0010 # color defined as 3 floats
MAT_24BIT_COLOR = 0x0011 # color defined as 3 bytes
@@ -211,7 +218,7 @@ def skip_to_end(file, skip_chunk):
skip_chunk.bytes_read += buffer_size
-def add_texture_to_material(image, texture, material, mapto):
+def add_texture_to_material(image, texture, scale, offset, extension, material, mapto):
#print('assigning %s to %s' % (texture, material))
if mapto not in {'COLOR', 'SPECULARITY', 'ALPHA', 'NORMAL'}:
@@ -226,6 +233,18 @@ def add_texture_to_material(image, texture, material, mapto):
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = False
+ mtex.scale = (scale[0], scale[1], 1.0)
+ mtex.offset = (offset[0], offset[1], 0.0)
+
+ texture.extension = 'REPEAT'
+ if extension == 'mirror':
+ # 3DS mirror flag can be emulated by these settings (at least so it seems)
+ texture.repeat_x = texture.repeat_y = 2
+ texture.use_mirror_x = texture.use_mirror_y = True
+ elif extension == 'decal':
+ # 3DS' decal mode maps best to Blenders CLIP
+ texture.extension = 'CLIP'
+
if mapto == 'COLOR':
mtex.use_map_color_diffuse = True
elif mapto == 'SPECULARITY':
@@ -255,6 +274,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
# TEXMODE = Mesh.FaceModes['TEX']
# Localspace variable names, faster.
+ STRUCT_SIZE_FLOAT = struct.calcsize('f')
STRUCT_SIZE_2FLOAT = struct.calcsize('2f')
STRUCT_SIZE_3FLOAT = struct.calcsize('3f')
STRUCT_SIZE_4FLOAT = struct.calcsize('4f')
@@ -330,7 +350,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
uvl[pl.loop_start].uv = contextMeshUV[v1 * 2: (v1 * 2) + 2]
uvl[pl.loop_start + 1].uv = contextMeshUV[v2 * 2: (v2 * 2) + 2]
- uvl[pl.loop_start + 2].uv = contextMeshUV[v3 * 2: ( v3 * 2) + 2]
+ uvl[pl.loop_start + 2].uv = contextMeshUV[v3 * 2: (v3 * 2) + 2]
# always a tri
bmesh.validate()
@@ -352,10 +372,20 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
CreateBlenderObject = False
def read_float_color(temp_chunk):
- temp_data = file.read(struct.calcsize('3f'))
- temp_chunk.bytes_read += 12
+ temp_data = file.read(STRUCT_SIZE_3FLOAT)
+ temp_chunk.bytes_read += STRUCT_SIZE_3FLOAT
return [float(col) for col in struct.unpack('<3f', temp_data)]
+ def read_float(temp_chunk):
+ temp_data = file.read(STRUCT_SIZE_FLOAT)
+ temp_chunk.bytes_read += STRUCT_SIZE_FLOAT
+ return struct.unpack('<f', temp_data)[0]
+
+ def read_short(temp_chunk):
+ temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+ temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
+ return struct.unpack('<H', temp_data)[0]
+
def read_byte_color(temp_chunk):
temp_data = file.read(struct.calcsize('3B'))
temp_chunk.bytes_read += 3
@@ -364,24 +394,46 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
def read_texture(new_chunk, temp_chunk, name, mapto):
new_texture = bpy.data.textures.new(name, type='IMAGE')
- img = None
+ u_scale, v_scale, u_offset, v_offset = 1.0, 1.0, 0.0, 0.0
+ mirror = False
+ extension = 'wrap'
while (new_chunk.bytes_read < new_chunk.length):
#print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length
read_chunk(file, temp_chunk)
if temp_chunk.ID == MAT_MAP_FILEPATH:
texture_name, read_str_len = read_string(file)
+
img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname)
- new_chunk.bytes_read += read_str_len # plus one for the null character that gets removed
+ temp_chunk.bytes_read += read_str_len # plus one for the null character that gets removed
- else:
- skip_to_end(file, temp_chunk)
+ elif temp_chunk.ID == MAT_MAP_USCALE:
+ u_scale = read_float(temp_chunk)
+ elif temp_chunk.ID == MAT_MAP_VSCALE:
+ v_scale = read_float(temp_chunk)
+
+ elif temp_chunk.ID == MAT_MAP_UOFFSET:
+ u_offset = read_float(temp_chunk)
+ elif temp_chunk.ID == MAT_MAP_VOFFSET:
+ v_offset = read_float(temp_chunk)
+
+ elif temp_chunk.ID == MAT_MAP_TILING:
+ tiling = read_short(temp_chunk)
+ if tiling & 0x2:
+ extension = 'mirror'
+ elif tiling & 0x10:
+ extension = 'decal'
+
+ elif temp_chunk.ID == MAT_MAP_ANG:
+ print("\nwarning: ignoring UV rotation")
+ skip_to_end(file, temp_chunk)
new_chunk.bytes_read += temp_chunk.bytes_read
# add the map to the material in the right channel
if img:
- add_texture_to_material(img, new_texture, contextMaterial, mapto)
+ add_texture_to_material(img, new_texture, (u_scale, v_scale),
+ (u_offset, v_offset), extension, contextMaterial, mapto)
dirname = os.path.dirname(file.name)