Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion B3DParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def parse(self, filepath):
pos = self.f(2)
scale = self.f(2)
rot = self.f(1)[0]
data.append(dotdict({'name':name,'position':pos,'scale':scale,'rotation':rot}))
data.append(dotdict({'name':name,'position':pos,'scale':scale,'rotation':rot, 'flags':flags,'blend':blend}))
self.cb_data(chunk,{'textures':data})

elif chunk=='BRUS':
Expand Down Expand Up @@ -229,5 +229,7 @@ def dump(node, level=0):
data = B3DTree().parse(filepath) # json tree
import json
print(json.dumps(data, indent=1))
#f = open(filepath+".json", "w")
#f.write(json.dumps(data, indent=1))
#dump(data)

191 changes: 113 additions & 78 deletions export_b3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@
TRANS_MATRIX = mathutils.Matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
BONE_TRANS_MATRIX = mathutils.Matrix([[-1,0,0,0],[0,0,-1,0],[0,-1,0,0],[0,0,0,1]])

DEBUG = False
DEBUG = True
PROGRESS = True
PROGRESS_VERBOSE = False
PROGRESS_VERBOSE = True

tesselated_objects = {}

Expand Down Expand Up @@ -157,19 +157,29 @@ def write_b3d_file(filename, settings, objects=[]):

print("Exported in", (end - start))

# Keeping this function around as the vertex_colors API is deprecated and may disappear in the future
def getVertexColors(obj_data):
return obj_data.vertex_colors
return obj_data.color_attributes

def getFaceImage(face):
def getFaceImage(face, obj):
try:
material = bpy.data.materials[face.material_index]
texImage = material.node_tree.nodes["Image Texture"]
return texImage.image
except:
pass
mat_index = face.material_index
if mat_index >= len(obj.data.materials):
return None

material = obj.data.materials[mat_index]
if not material or not material.use_nodes:
return None

for node in material.node_tree.nodes:
if node.type == 'TEX_IMAGE' and node.image:
return node.image

except Exception as e:
print(f"getFaceImage error: {e}")

return None


# ==== Write TEXS Chunk ====
def write_texs(objects, settings):
global trimmed_paths
Expand Down Expand Up @@ -246,7 +256,23 @@ def write_texs(objects, settings):
enable_mipmaps=8
else:
enable_mipmaps=0
texture_flags[obj_count][iuvlayer] = tex_flag | enable_mipmaps
tiling = 0
mapping = 0
material = bpy.data.materials[face.material_index]
if material.node_tree:
texImage = material.node_tree.nodes["Image Texture"]
if texImage:
if texImage.extension is 'EXTEND':
tiling = 48
elif texImage.extension is 'MIRROR':
tiling = 24578

if texImage.projection is 'SPHERE':
mapping = 64
elif texImage.projection is 'BOX':
mapping = 128

texture_flags[obj_count][iuvlayer] = tex_flag + enable_mipmaps + tiling + mapping
set_wrote = 1

for face in data.polygons:
Expand All @@ -261,7 +287,7 @@ def write_texs(objects, settings):

#if DEBUG: print("<uv face=", face.index, ">")

img = getFaceImage(face)
img = getFaceImage(face,obj)
if img:
if img.filepath in trimmed_paths:
img_name = trimmed_paths[img.filepath]
Expand Down Expand Up @@ -332,38 +358,36 @@ def write_brus(objects, settings):
for face in data.polygons:
face_stack = []

for iuvlayer,uvlayer in enumerate(uv_textures):
if iuvlayer < 8:
for i, iuvlayer in enumerate(uv_textures):
if i < 8:

img_id = -1

if face.index >= len(uv_textures[iuvlayer].data):
continue
# if face.index >= len(uv_textures[iuvlayer].data):
# continue

img = getFaceImage(face)
img = getFaceImage(face,obj)

if not img:
continue

img_found = 1
if img:
img_found = 1

if img.filepath in trimmed_paths:
img_name = trimmed_paths[img.filepath]
else:
img_name = os.path.basename(img.filepath)
trimmed_paths[img.filepath] = img_name
if img.filepath in trimmed_paths:
img_name = trimmed_paths[img.filepath]
else:
img_name = os.path.basename(img.filepath)
trimmed_paths[img.filepath] = img_name

if DEBUG: print(" <!-- Building FACE 'stack' -->")
if DEBUG: print(" <!-- Building FACE 'stack' -->")

if img_name in texs_stack:
img_id = texs_stack[img_name][TEXTURE_ID]
if img_name in texs_stack:
img_id = texs_stack[img_name][TEXTURE_ID]

face_stack.insert(iuvlayer,img_id)
if DEBUG: print(" <uv face=",face.index,"layer=", iuvlayer, " imgid=", img_id, "/>")
face_stack.insert(i,img_id)
if DEBUG: print(" <uv face=",face.index,"layer=", iuvlayer, " imgid=", img_id, "/>")

for i in range(len(face_stack),texture_count):
face_stack.append(-1)

img_found = any(i >= 0 for i in face_stack)

if DEBUG: print(" <!-- Writing chunk -->")

Expand All @@ -376,27 +400,31 @@ def write_brus(objects, settings):
mat_colb = mat_data.diffuse_color[2]
mat_alpha = 1.0 # mat_data.alpha # 2.8 fail!
mat_name = mat_data.name

mat_shine = abs(-1.0 + mat_data.roughness)
mat_fx = 0
if mat_data.use_backface_culling is True:
mat_fx += 16

if not mat_name in brus_stack:
brus_stack.append(mat_name)
temp_buf += write_string(mat_name) #Brush Name
temp_buf += write_float(mat_colr) #Red
temp_buf += write_float(mat_colg) #Green
temp_buf += write_float(mat_colb) #Blue
temp_buf += write_float(mat_alpha) #Alpha
temp_buf += write_float(0) #Shininess
temp_buf += write_float(mat_shine) #Shininess
temp_buf += write_int(1) #Blend
if settings.get("export_colors") and len(getVertexColors(data)):
temp_buf += write_int(2) #Fx
else:
temp_buf += write_int(0) #Fx

mat_fx += 2 #Fx
temp_buf += write_int(mat_fx)
for i in face_stack:
temp_buf += write_int(i) #Texture ID
else:
if settings.get("export_colors") and len(getVertexColors(data)) > 0:
if not face_stack in brus_stack:
brus_stack.append(face_stack)
face_stack_key = tuple(face_stack)
if face_stack_key not in brus_stack:
brus_stack.append(face_stack_key)
mat_count += 1
temp_buf += write_string("Brush.%.3i"%mat_count) #Brush Name
temp_buf += write_float(1) #Red
Expand All @@ -410,30 +438,42 @@ def write_brus(objects, settings):
for i in face_stack:
temp_buf += write_int(i) #Texture ID
else: # img_found
if data.materials:
if data.materials[face.material_index]:
mat_data = data.materials[face.material_index]
mat_colr = mat_data.diffuse_color[0]
mat_colg = mat_data.diffuse_color[1]
mat_colb = mat_data.diffuse_color[2]
mat_alpha = 1.0 # mat_data.alpha # 2.8 fail!
mat_name = mat_data.name
mat_shine = abs(-1.0 + mat_data.roughness)
mat_fx = 0
if mat_data.use_backface_culling is True:
mat_fx += 16

if not face_stack in brus_stack:
brus_stack.append(face_stack)
mat_count += 1
temp_buf += write_string(mat_name) #Brush Name
temp_buf += write_float(mat_colr) #Red
temp_buf += write_float(mat_colg) #Green
temp_buf += write_float(mat_colb) #Blue
temp_buf += write_float(mat_alpha) #Alpha
temp_buf += write_float(mat_shine) #Shininess
temp_buf += write_int(1) #Blend
if DEBUG: print(" <brush id=",len(brus_stack),">")

if not face_stack in brus_stack:
brus_stack.append(face_stack)
mat_count += 1
temp_buf += write_string("Brush.%.3i"%mat_count) #Brush Name
temp_buf += write_float(1) #Red
temp_buf += write_float(1) #Green
temp_buf += write_float(1) #Blue
temp_buf += write_float(1) #Alpha
temp_buf += write_float(0) #Shininess
temp_buf += write_int(1) #Blend

if DEBUG: print(" <brush id=",len(brus_stack),">")
if settings.get("export_colors") and len(getVertexColors(data)) > 0:
mat_fx += 2 #Fx


if settings.get("export_colors") and len(getVertexColors(data)) > 0:
temp_buf += write_int(2) #Fx
else:
temp_buf += write_int(0) #Fx
temp_buf += write_int(mat_fx) #Fx

for i in face_stack:
temp_buf += write_int(i) #Texture ID
if DEBUG: print(" <texture id=",i,">")
for i in face_stack:
temp_buf += write_int(i) #Texture ID
if DEBUG: print(" <texture id=",i,">")

if DEBUG: print(" </brush>")
if DEBUG: print(" </brush>")

if DEBUG: print("")

Expand Down Expand Up @@ -840,7 +880,7 @@ def write_node_mesh(settings, obj, arm_action):

temp_buf += write_int(-1) #Brush ID
temp_buf += write_node_mesh_vrts(settings, obj, data, arm_action) #NODE MESH VRTS
temp_buf += write_node_mesh_tris(data) #NODE MESH TRIS
temp_buf += write_node_mesh_tris(obj,data) #NODE MESH TRIS

if len(temp_buf) > 0:
mesh_buf += write_chunk(b"MESH",temp_buf)
Expand Down Expand Up @@ -946,19 +986,14 @@ def write_node_mesh_vrts(settings, obj, data, arm_action):

if settings.get("export_colors") and len(getVertexColors(data)) > 0:
vertex_colors = getVertexColors(data)
if vertex_id == 0:
vcolor = vertex_colors[0].data[face.index].color1
elif vertex_id == 1:
vcolor = vertex_colors[0].data[face.index].color2
elif vertex_id == 2:
vcolor = vertex_colors[0].data[face.index].color3
elif vertex_id == 3:
vcolor = vertex_colors[0].data[face.index].color4

temp_buf.append(write_float_quad(vcolor.r, #R
vcolor.g, #G
vcolor.b, #B
1.0)) #A (FIXME?)
color_data = vertex_colors[0].data
# get the color for this face corner
vcolor = color_data[face.loop_start + vertex_id].color
r, g, b, a = vcolor
temp_buf.append(write_float_quad(r, #R
g, #G
b, #B
a)) #A (FIXME?)

for vg in obj.vertex_groups:
w = 0.0
Expand All @@ -975,8 +1010,9 @@ def write_node_mesh_vrts(settings, obj, data, arm_action):
# uv_layers_count is from data.uv_layers

for iuvlayer in range(uv_layers_count):
uv = my_uvs[face.index][vertex_id]
temp_buf.append(write_float_couple(uv[0], 1-uv[1]) )
uvlayer = data.uv_layers[iuvlayer]
uv = uvlayer.data[loop_index].uv
temp_buf.append(write_float_couple(uv[0], 1 - uv[1]))

if DEBUG: print("")

Expand All @@ -987,7 +1023,7 @@ def write_node_mesh_vrts(settings, obj, data, arm_action):
return vrts_buf

# ==== Write NODE MESH TRIS Chunk ====
def write_node_mesh_tris(data):
def write_node_mesh_tris(obj,data):
global texture_count

# An dictoriary that maps all brush-ids to a list of faces
Expand All @@ -996,7 +1032,6 @@ def write_node_mesh_tris(data):
dBrushId2Face = {}

if DEBUG: print("")

for face in data.polygons:
img_found = 0
face_stack = []
Expand All @@ -1010,7 +1045,7 @@ def write_node_mesh_tris(data):

img_id = -1

img = getFaceImage(face)
img = getFaceImage(face,obj)

if img:
if img.filepath in trimmed_paths:
Expand Down
Loading