Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
车家大少爷
three.js
提交
51fdb4c7
T
three.js
项目概览
车家大少爷
/
three.js
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
three.js
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
51fdb4c7
编写于
2月 10, 2015
作者:
M
Mr.doob
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #6048 from repsac/io_three
Updating the Blender exporter
上级
8ff2776e
efb1ee8c
变更
22
展开全部
隐藏空白更改
内联
并排
Showing
22 changed file
with
1717 addition
and
819 deletion
+1717
-819
utils/exporters/blender/.gitignore
utils/exporters/blender/.gitignore
+1
-0
utils/exporters/blender/addons/io_three/__init__.py
utils/exporters/blender/addons/io_three/__init__.py
+372
-196
utils/exporters/blender/addons/io_three/constants.py
utils/exporters/blender/addons/io_three/constants.py
+19
-7
utils/exporters/blender/addons/io_three/exporter/api/animation.py
...porters/blender/addons/io_three/exporter/api/animation.py
+476
-0
utils/exporters/blender/addons/io_three/exporter/api/mesh.py
utils/exporters/blender/addons/io_three/exporter/api/mesh.py
+200
-311
utils/exporters/blender/addons/io_three/exporter/api/object.py
.../exporters/blender/addons/io_three/exporter/api/object.py
+55
-37
utils/exporters/blender/addons/io_three/exporter/base_classes.py
...xporters/blender/addons/io_three/exporter/base_classes.py
+70
-22
utils/exporters/blender/addons/io_three/exporter/geometry.py
utils/exporters/blender/addons/io_three/exporter/geometry.py
+163
-90
utils/exporters/blender/addons/io_three/exporter/image.py
utils/exporters/blender/addons/io_three/exporter/image.py
+23
-2
utils/exporters/blender/addons/io_three/exporter/io.py
utils/exporters/blender/addons/io_three/exporter/io.py
+50
-13
utils/exporters/blender/addons/io_three/exporter/material.py
utils/exporters/blender/addons/io_three/exporter/material.py
+33
-25
utils/exporters/blender/addons/io_three/exporter/object.py
utils/exporters/blender/addons/io_three/exporter/object.py
+45
-38
utils/exporters/blender/addons/io_three/exporter/scene.py
utils/exporters/blender/addons/io_three/exporter/scene.py
+122
-66
utils/exporters/blender/addons/io_three/exporter/texture.py
utils/exporters/blender/addons/io_three/exporter/texture.py
+8
-1
utils/exporters/blender/addons/io_three/exporter/utilities.py
...s/exporters/blender/addons/io_three/exporter/utilities.py
+54
-5
utils/exporters/blender/addons/io_three/logger.py
utils/exporters/blender/addons/io_three/logger.py
+7
-0
utils/exporters/blender/tests/blend/anim.blend
utils/exporters/blender/tests/blend/anim.blend
+0
-0
utils/exporters/blender/tests/blend/scene_children.blend
utils/exporters/blender/tests/blend/scene_children.blend
+0
-0
utils/exporters/blender/tests/scripts/js/review.js
utils/exporters/blender/tests/scripts/js/review.js
+8
-4
utils/exporters/blender/tests/scripts/test_geometry_normals.bash
...xporters/blender/tests/scripts/test_geometry_normals.bash
+1
-1
utils/exporters/blender/tests/scripts/test_scene_children.bash
.../exporters/blender/tests/scripts/test_scene_children.bash
+9
-0
utils/exporters/blender/tests/scripts/test_scene_instancing.bash
...xporters/blender/tests/scripts/test_scene_instancing.bash
+1
-1
未找到文件。
utils/exporters/blender/.gitignore
浏览文件 @
51fdb4c7
tests/review
__pycache__/
tmp/
utils/exporters/blender/addons/io_three/__init__.py
浏览文件 @
51fdb4c7
此差异已折叠。
点击以展开。
utils/exporters/blender/addons/io_three/constants.py
浏览文件 @
51fdb4c7
...
...
@@ -34,6 +34,7 @@ MAPPING_TYPES = type('Mapping', (), {
JSON
=
'json'
EXTENSION
=
'.%s'
%
JSON
INDENT
=
'indent'
MATERIALS
=
'materials'
...
...
@@ -49,8 +50,11 @@ SCALE = 'scale'
COMPRESSION
=
'compression'
MAPS
=
'maps'
FRAME_STEP
=
'frameStep'
ANIMATION
=
'animation'
FRAME_INDEX_AS_TIME
=
'frameIndexAsTime'
ANIMATION
=
'animations'
MORPH_TARGETS
=
'morphTargets'
POSE
=
'pose'
REST
=
'rest'
SKIN_INDICES
=
'skinIndices'
SKIN_WEIGHTS
=
'skinWeights'
LOGGING
=
'logging'
...
...
@@ -64,6 +68,7 @@ PRECISION = 'precision'
DEFAULT_PRECISION
=
6
EMBED_GEOMETRY
=
'embedGeometry'
EMBED_ANIMATION
=
'embedAnimation'
OFF
=
'off'
GLOBAL
=
'global'
BUFFER_GEOMETRY
=
'BufferGeometry'
...
...
@@ -93,11 +98,12 @@ EXPORT_OPTIONS = {
FACE_MATERIALS
:
False
,
SCALE
:
1
,
FRAME_STEP
:
1
,
SCENE
:
True
,
FRAME_INDEX_AS_TIME
:
False
,
SCENE
:
False
,
MIX_COLORS
:
False
,
COMPRESSION
:
None
,
MAPS
:
False
,
ANIMATION
:
False
,
ANIMATION
:
OFF
,
BONES
:
False
,
SKINNING
:
False
,
MORPH_TARGETS
:
False
,
...
...
@@ -105,12 +111,13 @@ EXPORT_OPTIONS = {
LIGHTS
:
False
,
COPY_TEXTURES
:
True
,
LOGGING
:
DEBUG
,
ENABLE_PRECISION
:
Fals
e
,
ENABLE_PRECISION
:
Tru
e
,
PRECISION
:
DEFAULT_PRECISION
,
EMBED_GEOMETRY
:
True
,
EMBED_ANIMATION
:
True
,
GEOMETRY_TYPE
:
GEOMETRY
,
INFLUENCES_PER_VERTEX
:
2
INFLUENCES_PER_VERTEX
:
2
,
INDENT
:
True
}
...
...
@@ -210,10 +217,15 @@ IMAGE = 'image'
NAME
=
'name'
PARENT
=
'parent'
#@TODO move to api.constants?
LENGTH
=
'length'
FPS
=
'fps'
HIERARCHY
=
'hierarchy'
POS
=
'pos'
ROTQ
=
'rotq'
ROT
=
'rot'
SCL
=
'scl'
TIME
=
'time'
KEYS
=
'keys'
AMBIENT
=
'ambient'
COLOR
=
'color'
...
...
utils/exporters/blender/addons/io_three/exporter/api/animation.py
0 → 100644
浏览文件 @
51fdb4c7
import
mathutils
from
bpy
import
data
,
context
from
..
import
constants
,
logger
,
utilities
def
pose_animation
(
armature
,
options
):
logger
.
debug
(
'animation.pose_animation %s'
,
armature
)
func
=
_parse_pose_action
return
_parse_action
(
func
,
armature
,
options
)
def
rest_animation
(
armature
,
options
):
logger
.
debug
(
'animation.rest_animation %s'
,
armature
)
func
=
_parse_rest_action
return
_parse_action
(
func
,
armature
,
options
)
def
_parse_action
(
func
,
armature
,
options
):
animations
=
[]
logger
.
info
(
'Parsing %d actions'
,
len
(
data
.
actions
))
round_off
,
round_val
=
utilities
.
rounding
(
options
)
for
action
in
data
.
actions
:
logger
.
info
(
'Parsing action %s'
,
action
.
name
)
animation
=
func
(
action
,
armature
,
options
,
round_off
,
round_val
)
animations
.
append
(
animation
)
return
animations
def
_parse_rest_action
(
action
,
armature
,
options
,
round_off
,
round_val
):
end_frame
=
action
.
frame_range
[
1
]
start_frame
=
action
.
frame_range
[
0
]
frame_length
=
end_frame
-
start_frame
l
,
r
,
s
=
armature
.
matrix_world
.
decompose
()
rotation_matrix
=
r
.
to_matrix
()
hierarchy
=
[]
parent_index
=
-
1
frame_step
=
options
.
get
(
constants
.
FRAME_STEP
,
1
)
fps
=
context
.
scene
.
render
.
fps
start
=
int
(
start_frame
)
end
=
int
(
end_frame
/
frame_step
)
+
1
for
bone
in
armature
.
data
.
bones
:
# I believe this was meant to skip control bones, may
# not be useful. needs more testing
if
bone
.
use_deform
is
False
:
logger
.
info
(
'Skipping animation data for bone %s'
,
bone
.
name
)
continue
logger
.
info
(
'Parsing animation data for bone %s'
,
bone
.
name
)
keys
=
[]
for
frame
in
range
(
start
,
end
):
computed_frame
=
frame
*
frame_step
pos
,
pchange
=
_position
(
bone
,
computed_frame
,
action
,
armature
.
matrix_world
)
rot
,
rchange
=
_rotation
(
bone
,
computed_frame
,
action
,
rotation_matrix
)
# flip y and z
px
,
py
,
pz
=
pos
.
x
,
pos
.
z
,
-
pos
.
y
rx
,
ry
,
rz
,
rw
=
rot
.
x
,
rot
.
z
,
-
rot
.
y
,
rot
.
w
if
frame
==
start_frame
:
time
=
(
frame
*
frame_step
-
start_frame
)
/
fps
#@TODO: missing scale values
keyframe
=
{
constants
.
TIME
:
time
,
constants
.
POS
:
[
px
,
py
,
pz
],
constants
.
ROT
:
[
rx
,
ry
,
rz
,
rw
],
constants
.
SCL
:
[
1
,
1
,
1
]
}
keys
.
append
(
keyframe
)
# END-FRAME: needs pos, rot and scl attributes
# with animation length (required frame)
elif
frame
==
end_frame
/
frame_step
:
time
=
frame_length
/
fps
keyframe
=
{
constants
.
TIME
:
time
,
constants
.
POS
:
[
px
,
py
,
pz
],
constants
.
ROT
:
[
rx
,
ry
,
rz
,
rw
],
constants
.
SCL
:
[
1
,
1
,
1
]
}
keys
.
append
(
keyframe
)
# MIDDLE-FRAME: needs only one of the attributes,
# can be an empty frame (optional frame)
elif
pchange
==
True
or
rchange
==
True
:
time
=
(
frame
*
frame_step
-
start_frame
)
/
fps
if
pchange
==
True
and
rchange
==
True
:
keyframe
=
{
constants
.
TIME
:
time
,
constants
.
POS
:
[
px
,
py
,
pz
],
constants
.
ROT
:
[
rx
,
ry
,
rz
,
rw
]
}
elif
pchange
==
True
:
keyframe
=
{
constants
.
TIME
:
time
,
constants
.
POS
:
[
px
,
py
,
pz
]
}
elif
rchange
==
True
:
keyframe
=
{
constants
.
TIME
:
time
,
constants
.
ROT
:
[
rx
,
ry
,
rz
,
rw
]
}
keys
.
append
(
keyframe
)
hierarchy
.
append
({
constants
.
KEYS
:
keys
,
constants
.
PARENT
:
parent_index
})
parent_index
+=
1
animation
=
{
constants
.
HIERARCHY
:
hierarchy
,
constants
.
LENGTH
:
frame_length
/
fps
,
constants
.
FPS
:
fps
,
constants
.
NAME
:
action
.
name
}
return
animation
def
_parse_pose_action
(
action
,
armature
,
options
,
round_off
,
round_val
):
#@TODO: this seems to fail in batch mode meaning the
# user has to have th GUI open. need to improve
# this logic to allow batch processing, if Blender
# chooses to behave....
current_context
=
context
.
area
.
type
context
.
area
.
type
=
'DOPESHEET_EDITOR'
context
.
space_data
.
mode
=
'ACTION'
context
.
area
.
spaces
.
active
.
action
=
action
armature_matrix
=
armature
.
matrix_world
fps
=
context
.
scene
.
render
.
fps
end_frame
=
action
.
frame_range
[
1
]
start_frame
=
action
.
frame_range
[
0
]
frame_length
=
end_frame
-
start_frame
frame_step
=
options
.
get
(
constants
.
FRAME_STEP
,
1
)
used_frames
=
int
(
frame_length
/
frame_step
)
+
1
keys
=
[]
channels_location
=
[]
channels_rotation
=
[]
channels_scale
=
[]
for
pose_bone
in
armature
.
pose
.
bones
:
logger
.
info
(
'Processing channels for %s'
,
pose_bone
.
bone
.
name
)
keys
.
append
([])
channels_location
.
append
(
_find_channels
(
action
,
pose_bone
.
bone
,
'location'
))
channels_rotation
.
append
(
_find_channels
(
action
,
pose_bone
.
bone
,
'rotation_quaternion'
))
channels_rotation
.
append
(
_find_channels
(
action
,
pose_bone
.
bone
,
'rotation_euler'
))
channels_scale
.
append
(
_find_channels
(
action
,
pose_bone
.
bone
,
'scale'
))
frame_step
=
options
[
constants
.
FRAME_STEP
]
frame_index_as_time
=
options
[
constants
.
FRAME_INDEX_AS_TIME
]
for
frame_index
in
range
(
0
,
used_frames
):
if
frame_index
==
used_frames
-
1
:
frame
=
end_frame
else
:
frame
=
start_frame
+
frame_index
*
frame_step
logger
.
info
(
'Processing frame %d'
,
frame
)
time
=
frame
-
start_frame
if
frame_index_as_time
is
False
:
time
=
time
/
fps
context
.
scene
.
frame_set
(
frame
)
bone_index
=
0
def
has_keyframe_at
(
channels
,
frame
):
def
find_keyframe_at
(
channel
,
frame
):
for
keyframe
in
channel
.
keyframe_points
:
if
keyframe
.
co
[
0
]
==
frame
:
return
keyframe
return
None
for
channel
in
channels
:
if
not
find_keyframe_at
(
channel
,
frame
)
is
None
:
return
True
return
False
for
pose_bone
in
armature
.
pose
.
bones
:
logger
.
info
(
'Processing bone %s'
,
pose_bone
.
bone
.
name
)
if
pose_bone
.
parent
is
None
:
bone_matrix
=
armature_matrix
*
pose_bone
.
matrix
else
:
parent_matrix
=
armature_matrix
*
pose_bone
.
parent
.
matrix
bone_matrix
=
armature_matrix
*
pose_bone
.
matrix
bone_matrix
=
parent_matrix
.
inverted
()
*
bone_matrix
pos
,
rot
,
scl
=
bone_matrix
.
decompose
()
pchange
=
True
or
has_keyframe_at
(
channels_location
[
bone_index
],
frame
)
rchange
=
True
or
has_keyframe_at
(
channels_rotation
[
bone_index
],
frame
)
schange
=
True
or
has_keyframe_at
(
channels_scale
[
bone_index
],
frame
)
if
round_off
:
pos
=
(
utilities
.
round_off
(
pos
.
x
,
round_val
),
utilities
.
round_off
(
pos
.
z
,
round_val
),
-
utilities
.
round_off
(
pos
.
y
,
round_val
)
)
rot
=
(
utilities
.
round_off
(
rot
.
x
,
round_val
),
utilities
.
round_off
(
rot
.
z
,
round_val
),
-
utilities
.
round_off
(
rot
.
y
,
round_val
),
utilities
.
round_off
(
rot
.
w
,
round_val
)
)
scl
=
(
utilities
.
round_off
(
scl
.
x
,
round_val
),
utilities
.
round_off
(
scl
.
z
,
round_val
),
utilities
.
round_off
(
scl
.
y
,
round_val
)
)
else
:
pos
=
(
pos
.
x
,
pos
.
z
,
-
pos
.
y
)
rot
=
(
rot
.
x
,
rot
.
z
,
-
rot
.
y
,
rot
.
w
)
scl
=
(
scl
.
x
,
scl
.
z
,
scl
.
y
)
keyframe
=
{
constants
.
TIME
:
time
}
if
frame
==
start_frame
or
frame
==
end_frame
:
keyframe
.
update
({
constants
.
POS
:
pos
,
constants
.
ROT
:
rot
,
constants
.
SCL
:
scl
})
elif
any
([
pchange
,
rchange
,
schange
]):
if
pchange
is
True
:
keyframe
[
constants
.
POS
]
=
pos
if
rchange
is
True
:
keyframe
[
constants
.
ROT
]
=
rot
if
schange
is
True
:
keyframe
[
constants
.
SCL
]
=
scl
if
len
(
keyframe
.
keys
())
>
1
:
logger
.
info
(
'Recording keyframe data for %s %s'
,
pose_bone
.
bone
.
name
,
str
(
keyframe
))
keys
[
bone_index
].
append
(
keyframe
)
else
:
logger
.
info
(
'No anim data to record for %s'
,
pose_bone
.
bone
.
name
)
bone_index
+=
1
hierarchy
=
[]
bone_index
=
0
for
pose_bone
in
armature
.
pose
.
bones
:
hierarchy
.
append
({
constants
.
PARENT
:
bone_index
-
1
,
constants
.
KEYS
:
keys
[
bone_index
]
})
bone_index
+=
1
if
frame_index_as_time
is
False
:
frame_length
=
frame_length
/
fps
context
.
scene
.
frame_set
(
start_frame
)
context
.
area
.
type
=
current_context
animation
=
{
constants
.
HIERARCHY
:
hierarchy
,
constants
.
LENGTH
:
frame_length
,
constants
.
FPS
:
fps
,
constants
.
NAME
:
action
.
name
}
return
animation
def
_find_channels
(
action
,
bone
,
channel_type
):
result
=
[]
if
len
(
action
.
groups
):
group_index
=
-
1
for
index
,
group
in
enumerate
(
action
.
groups
):
if
group
.
name
==
bone
.
name
:
group_index
=
index
#@TODO: break?
if
group_index
>
-
1
:
for
channel
in
action
.
groups
[
group_index
].
channels
:
if
channel_type
in
channel
.
data_path
:
result
.
append
(
channel
)
else
:
bone_label
=
'"%s"'
%
bone
.
name
for
channel
in
action
.
fcurves
:
data_path
=
[
bone_label
in
channel
.
data_path
,
channel_type
in
channel
.
data_path
]
if
all
(
data_path
):
result
.
append
(
channel
)
return
result
def
_position
(
bone
,
frame
,
action
,
armature_matrix
):
position
=
mathutils
.
Vector
((
0
,
0
,
0
))
change
=
False
ngroups
=
len
(
action
.
groups
)
if
ngroups
>
0
:
index
=
0
for
i
in
range
(
ngroups
):
if
action
.
groups
[
i
].
name
==
bone
.
name
:
index
=
i
for
channel
in
action
.
groups
[
index
].
channels
:
if
"location"
in
channel
.
data_path
:
has_changed
=
_handle_position_channel
(
channel
,
frame
,
position
)
change
=
change
or
has_changed
else
:
bone_label
=
'"%s"'
%
bone
.
name
for
channel
in
action
.
fcurves
:
data_path
=
channel
.
data_path
if
bone_label
in
data_path
and
\
"location"
in
data_path
:
has_changed
=
_handle_position_channel
(
channel
,
frame
,
position
)
change
=
change
or
has_changed
position
=
position
*
bone
.
matrix_local
.
inverted
()
if
bone
.
parent
is
None
:
position
.
x
+=
bone
.
head
.
x
position
.
y
+=
bone
.
head
.
y
position
.
z
+=
bone
.
head
.
z
else
:
parent
=
bone
.
parent
parent_matrix
=
parent
.
matrix_local
.
inverted
()
diff
=
parent
.
tail_local
-
parent
.
head_local
position
.
x
+=
(
bone
.
head
*
parent_matrix
).
x
+
diff
.
x
position
.
y
+=
(
bone
.
head
*
parent_matrix
).
y
+
diff
.
y
position
.
z
+=
(
bone
.
head
*
parent_matrix
).
z
+
diff
.
z
return
armature_matrix
*
position
,
change
def
_rotation
(
bone
,
frame
,
action
,
armature_matrix
):
# TODO: calculate rotation also from rotation_euler channels
rotation
=
mathutils
.
Vector
((
0
,
0
,
0
,
1
))
change
=
False
ngroups
=
len
(
action
.
groups
)
# animation grouped by bones
if
ngroups
>
0
:
index
=
-
1
for
i
in
range
(
ngroups
):
if
action
.
groups
[
i
].
name
==
bone
.
name
:
index
=
i
if
index
>
-
1
:
for
channel
in
action
.
groups
[
index
].
channels
:
if
"quaternion"
in
channel
.
data_path
:
has_changed
=
_handle_rotation_channel
(
channel
,
frame
,
rotation
)
change
=
change
or
has_changed
# animation in raw fcurves
else
:
bone_label
=
'"%s"'
%
bone
.
name
for
channel
in
action
.
fcurves
:
data_path
=
channel
.
data_path
if
bone_label
in
data_path
and
\
"quaternion"
in
data_path
:
has_changed
=
_handle_rotation_channel
(
channel
,
frame
,
rotation
)
change
=
change
or
has_changed
rot3
=
rotation
.
to_3d
()
rotation
.
xyz
=
rot3
*
bone
.
matrix_local
.
inverted
()
rotation
.
xyz
=
armature_matrix
*
rotation
.
xyz
return
rotation
,
change
def
_handle_rotation_channel
(
channel
,
frame
,
rotation
):
change
=
False
if
channel
.
array_index
in
[
0
,
1
,
2
,
3
]:
for
keyframe
in
channel
.
keyframe_points
:
if
keyframe
.
co
[
0
]
==
frame
:
change
=
True
value
=
channel
.
evaluate
(
frame
)
if
channel
.
array_index
==
1
:
rotation
.
x
=
value
elif
channel
.
array_index
==
2
:
rotation
.
y
=
value
elif
channel
.
array_index
==
3
:
rotation
.
z
=
value
elif
channel
.
array_index
==
0
:
rotation
.
w
=
value
return
change
def
_handle_position_channel
(
channel
,
frame
,
position
):
change
=
False
if
channel
.
array_index
in
[
0
,
1
,
2
]:
for
keyframe
in
channel
.
keyframe_points
:
if
keyframe
.
co
[
0
]
==
frame
:
change
=
True
value
=
channel
.
evaluate
(
frame
)
if
channel
.
array_index
==
0
:
position
.
x
=
value
if
channel
.
array_index
==
1
:
position
.
y
=
value
if
channel
.
array_index
==
2
:
position
.
z
=
value
return
change
utils/exporters/blender/addons/io_three/exporter/api/mesh.py
浏览文件 @
51fdb4c7
此差异已折叠。
点击以展开。
utils/exporters/blender/addons/io_three/exporter/api/object.py
浏览文件 @
51fdb4c7
...
...
@@ -17,7 +17,6 @@ from .constants import (
PERSP
,
ORTHO
,
RENDER
,
ZYX
,
NO_SHADOW
)
...
...
@@ -46,14 +45,16 @@ def _object(func):
return
inner
def
assemblies
(
valid_types
):
def
assemblies
(
valid_types
,
options
):
logger
.
debug
(
'object.assemblies(%s)'
,
valid_types
)
for
obj
in
data
.
objects
:
if
not
obj
.
parent
and
obj
.
type
in
valid_types
:
yield
obj
.
name
elif
obj
.
parent
and
not
obj
.
parent
.
parent
\
and
obj
.
parent
.
type
==
ARMATURE
:
# rigged assets are parented under armature nodes
if
obj
.
parent
and
obj
.
parent
.
type
!=
ARMATURE
:
continue
if
obj
.
parent
and
obj
.
parent
.
type
==
ARMATURE
:
logger
.
info
(
'Has armature parent %s'
,
obj
.
name
)
if
_valid_node
(
obj
,
valid_types
,
options
):
yield
obj
.
name
...
...
@@ -152,38 +153,15 @@ def node_type(obj):
def
nodes
(
valid_types
,
options
):
visible_layers
=
_visible_scene_layers
()
for
obj
in
data
.
objects
:
# skip objects that are not on visible layers
if
not
_on_visible_layer
(
obj
,
visible_layers
):
continue
try
:
export
=
obj
.
THREE_export
except
AttributeError
:
export
=
True
mesh_node
=
mesh
(
obj
,
options
)
is_mesh
=
obj
.
type
==
MESH
# skip objects that a mesh could not be resolved
if
is_mesh
and
not
mesh_node
:
continue
# secondary test; if a mesh node was resolved but no
# faces are detected then bow out
if
is_mesh
:
mesh_node
=
data
.
meshes
[
mesh_node
]
if
len
(
mesh_node
.
tessfaces
)
is
0
:
continue
if
obj
.
type
in
valid_types
and
export
:
if
_valid_node
(
obj
,
valid_types
,
options
):
yield
obj
.
name
@
_object
def
position
(
obj
,
options
):
logger
.
debug
(
'object.position(%s)'
,
obj
)
vector
=
_matrix
(
obj
)[
0
]
parent
=
obj
.
parent
is
None
vector
=
_decompose_matrix
(
obj
,
local
=
not
parent
)[
0
]
vector
=
(
vector
.
x
,
vector
.
y
,
vector
.
z
)
round_off
,
round_val
=
utilities
.
rounding
(
options
)
...
...
@@ -206,8 +184,8 @@ def receive_shadow(obj):
@
_object
def
rotation
(
obj
,
options
):
logger
.
debug
(
'object.rotation(%s)'
,
obj
)
vector
=
_
matrix
(
obj
)[
1
].
to_euler
(
ZYX
)
vector
=
(
vector
.
x
,
vector
.
y
,
vector
.
z
)
vector
=
_
decompose_matrix
(
obj
)[
1
]
vector
=
(
vector
.
x
,
vector
.
y
,
vector
.
z
,
vector
.
w
)
round_off
,
round_val
=
utilities
.
rounding
(
options
)
if
round_off
:
...
...
@@ -219,7 +197,7 @@ def rotation(obj, options):
@
_object
def
scale
(
obj
,
options
):
logger
.
debug
(
'object.scale(%s)'
,
obj
)
vector
=
_matrix
(
obj
)[
2
]
vector
=
_
decompose_
matrix
(
obj
)[
2
]
vector
=
(
vector
.
x
,
vector
.
y
,
vector
.
z
)
round_off
,
round_val
=
utilities
.
rounding
(
options
)
...
...
@@ -381,8 +359,11 @@ def extracted_meshes():
return
[
key
for
key
in
_MESH_MAP
.
keys
()]
def
_matrix
(
obj
):
matrix
=
ROTATE_X_PI2
*
obj
.
matrix_world
def
_decompose_matrix
(
obj
,
local
=
False
):
if
local
:
matrix
=
ROTATE_X_PI2
*
obj
.
matrix_local
else
:
matrix
=
ROTATE_X_PI2
*
obj
.
matrix_world
return
matrix
.
decompose
()
...
...
@@ -401,3 +382,40 @@ def _visible_scene_layers():
for
index
,
layer
in
enumerate
(
context
.
scene
.
layers
):
if
layer
:
visible_layers
.
append
(
index
)
return
visible_layers
def
_valid_node
(
obj
,
valid_types
,
options
):
if
obj
.
type
not
in
valid_types
:
return
False
# skip objects that are not on visible layers
visible_layers
=
_visible_scene_layers
()
if
not
_on_visible_layer
(
obj
,
visible_layers
):
return
False
try
:
export
=
obj
.
THREE_export
except
AttributeError
:
export
=
True
if
not
export
:
return
False
mesh_node
=
mesh
(
obj
,
options
)
is_mesh
=
obj
.
type
==
MESH
# skip objects that a mesh could not be resolved
if
is_mesh
and
not
mesh_node
:
return
False
# secondary test; if a mesh node was resolved but no
# faces are detected then bow out
if
is_mesh
:
mesh_node
=
data
.
meshes
[
mesh_node
]
if
len
(
mesh_node
.
tessfaces
)
is
0
:
return
False
# if we get this far assume that the mesh is valid
return
True
utils/exporters/blender/addons/io_three/exporter/base_classes.py
浏览文件 @
51fdb4c7
import
uuid
from
..
import
constants
,
exceptions
from
.
import
utilities
from
..
import
constants
,
exceptions
class
BaseClass
(
constants
.
BASE_DICT
):
"""Base class which inherits from a base dictionary object."""
_defaults
=
{}
def
__init__
(
self
,
parent
=
None
,
type
=
None
):
constants
.
BASE_DICT
.
__init__
(
self
)
self
.
_
_
type
=
type
self
.
_type
=
type
self
.
_
_
parent
=
parent
self
.
_parent
=
parent
constants
.
BASE_DICT
.
update
(
self
,
self
.
_defaults
.
copy
())
def
__setitem__
(
self
,
key
,
value
):
if
not
isinstance
(
value
,
constants
.
VALID_DATA_TYPES
):
msg
=
'Value is an invalid data type: %s'
%
type
(
value
)
raise
exceptions
.
ThreeValueError
(
msg
)
msg
=
"Value is an invalid data type: %s"
%
type
(
value
)
raise
exceptions
.
ThreeValueError
(
msg
)
constants
.
BASE_DICT
.
__setitem__
(
self
,
key
,
value
)
@
property
def
count
(
self
):
"""
:return: number of keys
:rtype: int
"""
return
len
(
self
.
keys
())
@
property
def
parent
(
self
):
return
self
.
__parent
"""
:return: parent object
"""
return
self
.
_parent
@
property
def
type
(
self
):
return
self
.
__type
"""
:return: the type (if applicable)
"""
return
self
.
_type
def
copy
(
self
):
"""Copies the items to a standard dictionary object.
:rtype: dict
"""
data
=
{}
def
_dict_copy
(
old
,
new
):
"""Recursive function for processing all values
:param old:
:param new:
"""
for
key
,
value
in
old
.
items
():
if
isinstance
(
value
,
(
str
,
list
)):
new
[
key
]
=
value
[:]
...
...
@@ -51,12 +79,16 @@ class BaseClass(constants.BASE_DICT):
return
data
class
BaseNode
(
BaseClass
):
class
BaseNode
(
BaseClass
):
"""Base class for all nodes for the current platform."""
def
__init__
(
self
,
node
,
parent
,
type
):
BaseClass
.
__init__
(
self
,
parent
=
parent
,
type
=
type
)
self
.
__node
=
node
if
node
is
not
None
:
self
.
_node
=
node
if
node
is
None
:
self
[
constants
.
UUID
]
=
utilities
.
id
()
else
:
self
[
constants
.
NAME
]
=
node
self
[
constants
.
UUID
]
=
utilities
.
id_from_name
(
node
)
if
isinstance
(
parent
,
BaseScene
):
scene
=
parent
...
...
@@ -65,35 +97,51 @@ class BaseNode(BaseClass):
else
:
scene
=
None
self
.
_
_
scene
=
scene
self
.
_scene
=
scene
self
[
constants
.
UUID
]
=
str
(
uuid
.
uuid4
()).
upper
()
@
property
def
node
(
self
):
return
self
.
__node
"""
:return: name of the node
"""
return
self
.
_node
@
property
def
scene
(
self
):
return
self
.
__scene
"""
:return: returns the scene point
"""
return
self
.
_scene
@
property
def
options
(
self
):
"""
:return: export options
:retype: dict
"""
return
self
.
scene
.
options
class
BaseScene
(
BaseClass
):
"""Base class that scenes inherit from."""
def
__init__
(
self
,
filepath
,
options
):
BaseClass
.
__init__
(
self
,
type
=
constants
.
SCENE
)
self
.
_
_
filepath
=
filepath
self
.
_filepath
=
filepath
self
.
_
_
options
=
options
.
copy
()
self
.
_options
=
options
.
copy
()
@
property
def
filepath
(
self
):
return
self
.
_
_
filepath
return
self
.
_filepath
@
property
def
options
(
self
):
return
self
.
_
_
options
return
self
.
_options
utils/exporters/blender/addons/io_three/exporter/geometry.py
浏览文件 @
51fdb4c7
...
...
@@ -7,9 +7,10 @@ FORMAT_VERSION = 3
class
Geometry
(
base_classes
.
BaseNode
):
"""Class that wraps a single mesh/geometry node."""
def
__init__
(
self
,
node
,
parent
=
None
):
logger
.
debug
(
'Geometry().__init__(%s)'
,
node
)
logger
.
debug
(
"Geometry().__init__(%s)"
,
node
)
#@TODO: maybe better to have `three` constants for
# strings that are specific to `three` properties
geo_type
=
constants
.
GEOMETRY
.
title
()
...
...
@@ -18,23 +19,28 @@ class Geometry(base_classes.BaseNode):
if
opt_type
==
constants
.
BUFFER_GEOMETRY
:
geo_type
=
constants
.
BUFFER_GEOMETRY
elif
opt_type
!=
constants
.
GEOMETRY
:
logger
.
error
(
'Unknown geometry type %s'
,
opt_type
)
logger
.
error
(
"Unknown geometry type %s"
,
opt_type
)
logger
.
info
(
'Setting %s to "%s"'
,
node
,
geo_type
)
logger
.
info
(
"Setting %s to '%s'"
,
node
,
geo_type
)
self
.
_defaults
[
constants
.
TYPE
]
=
geo_type
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
=
parent
,
type
=
geo_type
)
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
=
parent
,
type
=
geo_type
)
@
property
def
animation_filename
(
self
):
"""Calculate the file name for the animation file
:return: base name for the file
"""
compression
=
self
.
options
.
get
(
constants
.
COMPRESSION
)
if
compression
in
(
None
,
constants
.
NONE
):
ext
=
constants
.
JSON
elif
compression
==
constants
.
MSGPACK
:
ext
=
constants
.
PACK
key
=
''
for
key
in
(
constants
.
MORPH_TARGETS
,
constants
.
ANIMATION
):
try
:
self
[
key
]
...
...
@@ -42,27 +48,32 @@ class Geometry(base_classes.BaseNode):
except
KeyError
:
pass
else
:
logger
.
info
(
'%s has no animation data'
,
self
.
node
)
logger
.
info
(
"%s has no animation data"
,
self
.
node
)
return
return
'%s.%s.%s'
%
(
self
.
node
,
key
,
ext
)
@
property
def
face_count
(
self
):
"""Parse the bit masks of the `faces` array.
:rtype: int
"""
try
:
faces
=
self
[
constants
.
FACES
]
except
KeyError
:
logger
.
debug
(
'No parsed faces found'
)
logger
.
debug
(
"No parsed faces found"
)
return
0
length
=
len
(
faces
)
offset
=
0
bitset
=
lambda
x
,
y
:
x
&
(
1
<<
y
)
bitset
=
lambda
x
,
y
:
x
&
(
1
<<
y
)
face_count
=
0
masks
=
(
constants
.
MASK
[
constants
.
UVS
],
constants
.
MASK
[
constants
.
NORMALS
],
constants
.
MASK
[
constants
.
COLORS
])
constants
.
MASK
[
constants
.
NORMALS
],
constants
.
MASK
[
constants
.
COLORS
])
while
offset
<
length
:
bit
=
faces
[
offset
]
...
...
@@ -84,20 +95,32 @@ class Geometry(base_classes.BaseNode):
@
property
def
metadata
(
self
):
"""Metadata for the current node.
:rtype: dict
"""
metadata
=
{
constants
.
GENERATOR
:
constants
.
THREE
,
constants
.
VERSION
:
FORMAT_VERSION
}
if
self
[
constants
.
TYPE
]
==
constants
.
GEOMETRY
.
title
():
self
.
_
_
geometry_metadata
(
metadata
)
self
.
_geometry_metadata
(
metadata
)
else
:
self
.
_
_
buffer_geometry_metadata
(
metadata
)
self
.
_buffer_geometry_metadata
(
metadata
)
return
metadata
def
copy
(
self
,
scene
=
True
):
logger
.
debug
(
'Geometry().copy(scene=%s)'
,
scene
)
"""Copy the geometry definitions to a standard dictionary.
:param scene: toggle for scene formatting (Default = True)
:type scene: bool
:rtype: dict
"""
logger
.
debug
(
"Geometry().copy(scene=%s)"
,
scene
)
dispatch
=
{
True
:
self
.
_scene_format
,
False
:
self
.
_geometry_format
...
...
@@ -107,47 +130,68 @@ class Geometry(base_classes.BaseNode):
try
:
data
[
constants
.
MATERIALS
]
=
self
[
constants
.
MATERIALS
].
copy
()
except
KeyError
:
logger
.
debug
(
'No materials to copy'
)
logger
.
debug
(
"No materials to copy"
)
return
data
def
copy_textures
(
self
):
logger
.
debug
(
'Geometry().copy_textures()'
)
"""Copy the textures to the destination directory."""
logger
.
debug
(
"Geometry().copy_textures()"
)
if
self
.
options
.
get
(
constants
.
COPY_TEXTURES
):
texture_registration
=
self
.
register_textures
()
if
texture_registration
:
logger
.
info
(
'%s has registered textures'
,
self
.
node
)
logger
.
info
(
"%s has registered textures"
,
self
.
node
)
io
.
copy_registered_textures
(
os
.
path
.
dirname
(
self
.
scene
.
filepath
),
texture_registration
)
def
parse
(
self
):
logger
.
debug
(
'Geometry().parse()'
)
"""Parse the current node"""
logger
.
debug
(
"Geometry().parse()"
)
if
self
[
constants
.
TYPE
]
==
constants
.
GEOMETRY
.
title
():
logger
.
info
(
'Parsing Geometry format'
)
self
.
_
_
parse_geometry
()
logger
.
info
(
"Parsing Geometry format"
)
self
.
_parse_geometry
()
else
:
logger
.
info
(
'Parsing BufferGeometry format'
)
self
.
_
_
parse_buffer_geometry
()
logger
.
info
(
"Parsing BufferGeometry format"
)
self
.
_parse_buffer_geometry
()
def
register_textures
(
self
):
logger
.
debug
(
'Geometry().register_textures()'
)
return
api
.
mesh
.
texture_registration
(
self
.
node
)
"""Obtain a texture registration object.
:rtype: dict
"""
logger
.
debug
(
"Geometry().register_textures()"
)
return
api
.
mesh
.
texture_registration
(
self
.
node
)
def
write
(
self
,
filepath
=
None
):
logger
.
debug
(
'Geometry().write(filepath=%s)'
,
filepath
)
"""Write the geometry definitions to disk. Uses the
desitnation path of the scene.
:param filepath: optional output file path (Default=None)
:type filepath: str
"""
logger
.
debug
(
"Geometry().write(filepath=%s)"
,
filepath
)
filepath
=
filepath
or
self
.
scene
.
filepath
io
.
dump
(
filepath
,
self
.
copy
(
scene
=
False
),
options
=
self
.
scene
.
options
)
io
.
dump
(
filepath
,
self
.
copy
(
scene
=
False
),
options
=
self
.
scene
.
options
)
if
self
.
options
.
get
(
constants
.
MAPS
):
logger
.
info
(
'Copying textures for %s'
,
self
.
node
)
logger
.
info
(
"Copying textures for %s"
,
self
.
node
)
self
.
copy_textures
()
def
write_animation
(
self
,
filepath
):
logger
.
debug
(
'Geometry().write_animation(%s)'
,
filepath
)
"""Write the animation definitions to a separate file
on disk. This helps optimize the geometry file size.
:param filepath: destination path
:type filepath: str
"""
logger
.
debug
(
"Geometry().write_animation(%s)"
,
filepath
)
for
key
in
(
constants
.
MORPH_TARGETS
,
constants
.
ANIMATION
):
try
:
...
...
@@ -156,29 +200,35 @@ class Geometry(base_classes.BaseNode):
except
KeyError
:
pass
else
:
logger
.
info
(
'%s has no animation data'
,
self
.
node
)
logger
.
info
(
"%s has no animation data"
,
self
.
node
)
return
filepath
=
os
.
path
.
join
(
filepath
,
self
.
animation_filename
)
if
filepath
:
logger
.
info
(
'Dumping animation data to %s'
,
filepath
)
logger
.
info
(
"Dumping animation data to %s"
,
filepath
)
io
.
dump
(
filepath
,
data
,
options
=
self
.
scene
.
options
)
return
filepath
else
:
logger
.
warning
(
'Could not determine a filepath for '
\
'animation data. Nothing written to disk.'
)
logger
.
warning
(
"Could not determine a filepath for "
\
"animation data. Nothing written to disk."
)
def
_component_data
(
self
):
logger
.
debug
(
'Geometry()._component_data()'
)
"""Query the component data only
:rtype: dict
"""
logger
.
debug
(
"Geometry()._component_data()"
)
if
self
[
constants
.
TYPE
]
!=
constants
.
GEOMETRY
.
title
():
return
self
[
constants
.
ATTRIBUTES
]
components
=
[
constants
.
VERTICES
,
constants
.
FACES
,
constants
.
UVS
,
constants
.
COLORS
,
constants
.
NORMALS
,
constants
.
BONES
,
constants
.
SKIN_WEIGHTS
,
constants
.
SKIN_INDICES
,
constants
.
NAME
,
constants
.
INFLUENCES_PER_VERTEX
]
components
=
[
constants
.
VERTICES
,
constants
.
FACES
,
constants
.
UVS
,
constants
.
COLORS
,
constants
.
NORMALS
,
constants
.
BONES
,
constants
.
SKIN_WEIGHTS
,
constants
.
SKIN_INDICES
,
constants
.
NAME
,
constants
.
INFLUENCES_PER_VERTEX
]
data
=
{}
anim_components
=
[
constants
.
MORPH_TARGETS
,
constants
.
ANIMATION
]
...
...
@@ -192,20 +242,25 @@ class Geometry(base_classes.BaseNode):
pass
else
:
data
[
component
]
=
os
.
path
.
basename
(
self
.
animation_filename
)
self
.
animation_filename
)
break
else
:
logger
.
info
(
'No animation data found for %s'
,
self
.
node
)
logger
.
info
(
"No animation data found for %s"
,
self
.
node
)
for
component
in
components
:
try
:
data
[
component
]
=
self
[
component
]
except
KeyError
:
logger
.
debug
(
'Component %s not found'
,
component
)
pass
logger
.
debug
(
"Component %s not found"
,
component
)
return
data
def
_geometry_format
(
self
):
"""Three.Geometry formatted definitions
:rtype: dict
"""
data
=
self
.
_component_data
()
if
self
[
constants
.
TYPE
]
!=
constants
.
GEOMETRY
.
title
():
...
...
@@ -219,17 +274,27 @@ class Geometry(base_classes.BaseNode):
return
data
def
__buffer_geometry_metadata
(
self
,
metadata
):
def
_buffer_geometry_metadata
(
self
,
metadata
):
"""Three.BufferGeometry metadata
:rtype: dict
"""
for
key
,
value
in
self
[
constants
.
ATTRIBUTES
].
items
():
size
=
value
[
constants
.
ITEM_SIZE
]
array
=
value
[
constants
.
ARRAY
]
metadata
[
key
]
=
len
(
array
)
/
size
def
__geometry_metadata
(
self
,
metadata
):
def
_geometry_metadata
(
self
,
metadata
):
"""Three.Geometry metadat
:rtype: dict
"""
skip
=
(
constants
.
TYPE
,
constants
.
FACES
,
constants
.
UUID
,
constants
.
ANIMATION
,
constants
.
SKIN_INDICES
,
constants
.
SKIN_WEIGHTS
,
constants
.
NAME
,
constants
.
INFLUENCES_PER_VERTEX
)
constants
.
ANIMATION
,
constants
.
SKIN_INDICES
,
constants
.
SKIN_WEIGHTS
,
constants
.
NAME
,
constants
.
INFLUENCES_PER_VERTEX
)
vectors
=
(
constants
.
VERTICES
,
constants
.
NORMALS
)
for
key
in
self
.
keys
():
...
...
@@ -249,6 +314,11 @@ class Geometry(base_classes.BaseNode):
metadata
[
constants
.
FACES
]
=
faces
def
_scene_format
(
self
):
"""Format the output for Scene compatability
:rtype: dict
"""
data
=
{
constants
.
UUID
:
self
[
constants
.
UUID
],
constants
.
TYPE
:
self
[
constants
.
TYPE
]
...
...
@@ -267,34 +337,35 @@ class Geometry(base_classes.BaseNode):
}
else
:
data
[
constants
.
ATTRIBUTES
]
=
component_data
data
[
constants
.
METADATA
]
=
self
.
metadata
data
[
constants
.
METADATA
]
=
self
.
metadata
data
[
constants
.
NAME
]
=
self
[
constants
.
NAME
]
return
data
return
data
def
__parse_buffer_geometry
(
self
):
def
_parse_buffer_geometry
(
self
):
"""Parse the geometry to Three.BufferGeometry specs"""
self
[
constants
.
ATTRIBUTES
]
=
{}
options_vertices
=
self
.
options
.
get
(
constants
.
VERTICES
)
option_normals
=
self
.
options
.
get
(
constants
.
NORMALS
)
option_uvs
=
self
.
options
.
get
(
constants
.
UVS
)
dispatch
=
(
(
constants
.
POSITION
,
options_vertices
,
api
.
mesh
.
buffer_position
,
3
),
(
constants
.
UV
,
option_uvs
,
api
.
mesh
.
buffer_uv
,
2
),
(
constants
.
NORMAL
,
option_normals
,
api
.
mesh
.
buffer_normal
,
3
)
)
pos_tuple
=
(
constants
.
POSITION
,
options_vertices
,
api
.
mesh
.
buffer_position
,
3
)
uvs_tuple
=
(
constants
.
UV
,
option_uvs
,
api
.
mesh
.
buffer_uv
,
2
)
normals_tuple
=
(
constants
.
NORMAL
,
option_normals
,
api
.
mesh
.
buffer_normal
,
3
)
dispatch
=
(
pos_tuple
,
uvs_tuple
,
normals_tuple
)
for
key
,
option
,
func
,
size
in
dispatch
:
for
key
,
option
,
func
,
size
in
dispatch
:
if
not
option
:
continue
array
=
func
(
self
.
node
,
self
.
options
)
if
not
array
:
logger
.
warning
(
'No array could be made for %s'
,
key
)
array
=
func
(
self
.
node
,
self
.
options
)
or
[]
if
not
array
:
logger
.
warning
(
"No array could be made for %s"
,
key
)
continue
self
[
constants
.
ATTRIBUTES
][
key
]
=
{
...
...
@@ -303,53 +374,55 @@ class Geometry(base_classes.BaseNode):
constants
.
ARRAY
:
array
}
def
__parse_geometry
(
self
):
def
_parse_geometry
(
self
):
"""Parse the geometry to Three.Geometry specs"""
if
self
.
options
.
get
(
constants
.
VERTICES
):
logger
.
info
(
'Parsing %s'
,
constants
.
VERTICES
)
logger
.
info
(
"Parsing %s"
,
constants
.
VERTICES
)
self
[
constants
.
VERTICES
]
=
api
.
mesh
.
vertices
(
self
.
node
,
self
.
options
)
if
self
.
options
.
get
(
constants
.
FACES
):
logger
.
info
(
'Parsing %s'
,
constants
.
FACES
)
self
[
constants
.
FACES
]
=
api
.
mesh
.
faces
(
self
.
node
,
self
.
options
)
self
.
node
,
self
.
options
)
or
[]
if
self
.
options
.
get
(
constants
.
NORMALS
):
logger
.
info
(
'Parsing %s'
,
constants
.
NORMALS
)
logger
.
info
(
"Parsing %s"
,
constants
.
NORMALS
)
self
[
constants
.
NORMALS
]
=
api
.
mesh
.
normals
(
self
.
node
,
self
.
options
)
self
.
node
,
self
.
options
)
or
[]
if
self
.
options
.
get
(
constants
.
COLORS
):
logger
.
info
(
'Parsing %s'
,
constants
.
COLORS
)
logger
.
info
(
"Parsing %s"
,
constants
.
COLORS
)
self
[
constants
.
COLORS
]
=
api
.
mesh
.
vertex_colors
(
self
.
node
)
self
.
node
)
or
[]
if
self
.
options
.
get
(
constants
.
FACE_MATERIALS
):
logger
.
info
(
'Parsing %s'
,
constants
.
FACE_MATERIALS
)
logger
.
info
(
"Parsing %s"
,
constants
.
FACE_MATERIALS
)
self
[
constants
.
MATERIALS
]
=
api
.
mesh
.
materials
(
self
.
node
,
self
.
options
)
self
.
node
,
self
.
options
)
or
[]
if
self
.
options
.
get
(
constants
.
UVS
):
logger
.
info
(
'Parsing %s'
,
constants
.
UVS
)
logger
.
info
(
"Parsing %s"
,
constants
.
UVS
)
self
[
constants
.
UVS
]
=
api
.
mesh
.
uvs
(
self
.
node
,
self
.
options
)
self
.
node
,
self
.
options
)
or
[]
if
self
.
options
.
get
(
constants
.
ANIMATION
):
logger
.
info
(
'Parsing %s'
,
constants
.
ANIMATION
)
self
[
constants
.
ANIMATION
]
=
api
.
mesh
.
animation
(
self
.
node
,
self
.
options
)
if
self
.
options
.
get
(
constants
.
FACES
):
logger
.
info
(
"Parsing %s"
,
constants
.
FACES
)
self
[
constants
.
FACES
]
=
api
.
mesh
.
faces
(
self
.
node
,
self
.
options
)
or
[]
no_anim
=
(
None
,
False
,
constants
.
OFF
)
if
self
.
options
.
get
(
constants
.
ANIMATION
)
not
in
no_anim
:
logger
.
info
(
"Parsing %s"
,
constants
.
ANIMATION
)
self
[
constants
.
ANIMATION
]
=
api
.
mesh
.
skeletal_animation
(
self
.
node
,
self
.
options
)
or
[]
#@TODO: considering making bones data implied when
# querying skinning data
bone_map
=
{}
if
self
.
options
.
get
(
constants
.
BONES
):
logger
.
info
(
'Parsing %s'
,
constants
.
BONES
)
bones
,
bone_map
=
api
.
mesh
.
bones
(
self
.
node
)
logger
.
info
(
"Parsing %s"
,
constants
.
BONES
)
bones
,
bone_map
=
api
.
mesh
.
bones
(
self
.
node
,
self
.
options
)
self
[
constants
.
BONES
]
=
bones
if
self
.
options
.
get
(
constants
.
SKINNING
):
logger
.
info
(
'Parsing %s'
,
constants
.
SKINNING
)
logger
.
info
(
"Parsing %s"
,
constants
.
SKINNING
)
influences
=
self
.
options
.
get
(
constants
.
INFLUENCES_PER_VERTEX
,
2
)
...
...
@@ -360,7 +433,7 @@ class Geometry(base_classes.BaseNode):
self
.
node
,
bone_map
,
influences
)
if
self
.
options
.
get
(
constants
.
MORPH_TARGETS
):
logger
.
info
(
'Parsing %s'
,
constants
.
MORPH_TARGETS
)
logger
.
info
(
"Parsing %s"
,
constants
.
MORPH_TARGETS
)
self
[
constants
.
MORPH_TARGETS
]
=
api
.
mesh
.
morph_targets
(
self
.
node
,
self
.
options
)
utils/exporters/blender/addons/io_three/exporter/image.py
浏览文件 @
51fdb4c7
...
...
@@ -4,22 +4,43 @@ from . import base_classes, io, api
class
Image
(
base_classes
.
BaseNode
):
"""Class the wraps an image node. This is the node that
represent that actual file on disk.
"""
def
__init__
(
self
,
node
,
parent
):
logger
.
debug
(
'Image().__init__(%s)'
,
node
)
logger
.
debug
(
"Image().__init__(%s)"
,
node
)
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
,
constants
.
IMAGE
)
self
[
constants
.
URL
]
=
api
.
image
.
file_name
(
self
.
node
)
@
property
def
destination
(
self
):
"""
:return: full destination path (when copied)
"""
dirname
=
os
.
path
.
dirname
(
self
.
scene
.
filepath
)
return
os
.
path
.
join
(
dirname
,
self
[
constants
.
URL
])
@
property
def
filepath
(
self
):
"""
:return: source file path
"""
return
api
.
image
.
file_path
(
self
.
node
)
def
copy_texture
(
self
,
func
=
io
.
copy
):
logger
.
debug
(
'Image().copy_texture()'
)
"""Copy the texture.
self.filepath > self.destination
:param func: Optional function override (Default = io.copy)
arguments are (<source>, <destination>)
:return: path the texture was copied to
"""
logger
.
debug
(
"Image().copy_texture()"
)
func
(
self
.
filepath
,
self
.
destination
)
return
self
.
destination
utils/exporters/blender/addons/io_three/exporter/io.py
浏览文件 @
51fdb4c7
import
os
import
shutil
from
..
import
constants
,
logger
from
.
import
_json
def
copy_registered_textures
(
dest
,
registration
):
logger
.
debug
(
'io.copy_registered_textures(%s, %s)'
,
dest
,
registration
)
"""Copy the registered textures to the destination (root) path
:param dest: destination directory
:param registration: registered textures
:type dest: str
:type registration: dict
"""
logger
.
debug
(
"io.copy_registered_textures(%s, %s)"
,
dest
,
registration
)
for
value
in
registration
.
values
():
copy
(
value
[
'file_path'
],
dest
)
def
copy
(
src
,
dst
):
logger
.
debug
(
'io.copy(%s, %s)'
%
(
src
,
dst
))
shutil
.
copy
(
src
,
dst
)
"""Copy a file to a destination
:param src: source file
:param dst: destination file/path
"""
logger
.
debug
(
"io.copy(%s, %s)"
%
(
src
,
dst
))
if
os
.
path
.
isdir
(
dst
):
file_name
=
os
.
path
.
basename
(
src
)
dst
=
os
.
path
.
join
(
dst
,
file_name
)
if
src
!=
dst
:
shutil
.
copy
(
src
,
dst
)
def
dump
(
filepath
,
data
,
options
=
None
):
"""Dump the output to disk (JSON, msgpack, etc)
:param filepath: output file path
:param data: serializable data to write to disk
:param options: (Default = None)
:type options: dict
"""
options
=
options
or
{}
logger
.
debug
(
'io.dump(%s, data, options=%s)'
,
filepath
,
options
)
logger
.
debug
(
"io.dump(%s, data, options=%s)"
,
filepath
,
options
)
compress
=
options
.
get
(
constants
.
COMPRESSION
,
constants
.
NONE
)
if
compress
==
constants
.
MSGPACK
:
try
:
import
msgpack
except
ImportError
:
logger
.
error
(
'msgpack module not found'
)
logger
.
error
(
"msgpack module not found"
)
raise
logger
.
info
(
'Dumping to msgpack'
)
func
=
lambda
x
,
y
:
msgpack
.
dump
(
x
,
y
)
logger
.
info
(
"Dumping to msgpack"
)
func
=
lambda
x
,
y
:
msgpack
.
dump
(
x
,
y
)
mode
=
'wb'
else
:
round_off
=
options
.
get
(
constants
.
ENABLE_PRECISION
)
...
...
@@ -36,28 +64,37 @@ def dump(filepath, data, options=None):
else
:
_json
.
ROUND
=
None
logger
.
info
(
'Dumping to JSON'
)
func
=
lambda
x
,
y
:
_json
.
json
.
dump
(
x
,
y
,
indent
=
4
)
indent
=
options
.
get
(
constants
.
INDENT
,
True
)
indent
=
4
if
indent
else
None
logger
.
info
(
"Dumping to JSON"
)
func
=
lambda
x
,
y
:
_json
.
json
.
dump
(
x
,
y
,
indent
=
indent
)
mode
=
'w'
logger
.
info
(
'Writing to %s'
,
filepath
)
logger
.
info
(
"Writing to %s"
,
filepath
)
with
open
(
filepath
,
mode
=
mode
)
as
stream
:
func
(
data
,
stream
)
def
load
(
filepath
,
options
):
logger
.
debug
(
'io.load(%s, %s)'
,
filepath
,
options
)
"""Load the contents of the file path with the correct parser
:param filepath: input file path
:param options:
:type options: dict
"""
logger
.
debug
(
"io.load(%s, %s)"
,
filepath
,
options
)
compress
=
options
.
get
(
constants
.
COMPRESSION
,
constants
.
NONE
)
if
compress
==
constants
.
MSGPACK
:
try
:
import
msgpack
except
ImportError
:
logger
.
error
(
'msgpack module not found'
)
logger
.
error
(
"msgpack module not found"
)
raise
module
=
msgpack
mode
=
'rb'
else
:
logger
.
info
(
'Loading JSON'
)
logger
.
info
(
"Loading JSON"
)
module
=
_json
.
json
mode
=
'r'
...
...
utils/exporters/blender/addons/io_three/exporter/material.py
浏览文件 @
51fdb4c7
...
...
@@ -3,21 +3,23 @@ from . import base_classes, utilities, api
class
Material
(
base_classes
.
BaseNode
):
"""Class that wraps material nodes"""
def
__init__
(
self
,
node
,
parent
):
logger
.
debug
(
'Material().__init__(%s)'
,
node
)
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
,
constants
.
MATERIAL
)
self
.
_
_
common_attributes
()
logger
.
debug
(
"Material().__init__(%s)"
,
node
)
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
,
constants
.
MATERIAL
)
self
.
_common_attributes
()
if
self
[
constants
.
TYPE
]
==
constants
.
THREE_PHONG
:
self
.
_
_
phong_attributes
()
self
.
_phong_attributes
()
textures
=
self
.
parent
.
options
.
get
(
constants
.
MAPS
)
if
textures
:
self
.
_
_
update_maps
()
self
.
_update_maps
()
def
__common_attributes
(
self
):
logger
.
debug
(
'Material().__common_attributes()'
)
def
_common_attributes
(
self
):
"""Parse the common material attributes"""
logger
.
debug
(
'Material()._common_attributes()'
)
dispatch
=
{
constants
.
PHONG
:
constants
.
THREE_PHONG
,
constants
.
LAMBERT
:
constants
.
THREE_LAMBERT
,
...
...
@@ -26,14 +28,15 @@ class Material(base_classes.BaseNode):
shader_type
=
api
.
material
.
type
(
self
.
node
)
self
[
constants
.
TYPE
]
=
dispatch
[
shader_type
]
ambient
=
api
.
material
.
ambient_color
(
self
.
node
)
self
[
constants
.
AMBIENT
]
=
utilities
.
rgb2int
(
ambient
)
diffuse
=
api
.
material
.
diffuse_color
(
self
.
node
)
self
[
constants
.
COLOR
]
=
utilities
.
rgb2int
(
diffuse
)
emissive
=
api
.
material
.
emissive_color
(
self
.
node
)
self
[
constants
.
EMISSIVE
]
=
utilities
.
rgb2int
(
emissive
)
if
self
[
constants
.
TYPE
]
!=
constants
.
THREE_BASIC
:
ambient
=
api
.
material
.
ambient_color
(
self
.
node
)
self
[
constants
.
AMBIENT
]
=
utilities
.
rgb2int
(
ambient
)
emissive
=
api
.
material
.
emissive_color
(
self
.
node
)
self
[
constants
.
EMISSIVE
]
=
utilities
.
rgb2int
(
emissive
)
vertex_color
=
api
.
material
.
use_vertex_colors
(
self
.
node
)
self
[
constants
.
VERTEX_COLORS
]
=
vertex_color
...
...
@@ -44,14 +47,18 @@ class Material(base_classes.BaseNode):
self
[
constants
.
DEPTH_WRITE
]
=
api
.
material
.
depth_write
(
self
.
node
)
def
__phong_attributes
(
self
):
logger
.
debug
(
'Material().__phong_attributes()'
)
def
_phong_attributes
(
self
):
"""Parse phong specific attributes"""
logger
.
debug
(
"Material()._phong_attributes()"
)
specular
=
api
.
material
.
specular_color
(
self
.
node
)
self
[
constants
.
SPECULAR
]
=
utilities
.
rgb2int
(
specular
)
self
[
constants
.
SHININESS
]
=
api
.
material
.
specular_coef
(
self
.
node
)
def
__update_maps
(
self
):
logger
.
debug
(
'Material().__update_maps()'
)
def
_update_maps
(
self
):
"""Parses maps/textures and updates the textures array
with any new nodes found.
"""
logger
.
debug
(
"Material()._update_maps()"
)
mapping
=
(
(
api
.
material
.
diffuse_map
,
constants
.
MAP
),
...
...
@@ -59,14 +66,14 @@ class Material(base_classes.BaseNode):
(
api
.
material
.
light_map
,
constants
.
LIGHT_MAP
)
)
for
func
,
key
in
mapping
:
for
func
,
key
in
mapping
:
map_node
=
func
(
self
.
node
)
if
map_node
:
logger
.
info
(
'Found map node %s for %s'
,
map_node
,
key
)
tex_inst
=
self
.
scene
.
texture
(
map_node
.
name
)
self
[
key
]
=
tex_inst
[
constants
.
UUID
]
self
[
key
]
=
tex_inst
[
constants
.
UUID
]
if
self
[
constants
.
TYPE
]
==
constants
.
THREE_PHONG
:
if
self
[
constants
.
TYPE
]
==
constants
.
THREE_PHONG
:
mapping
=
(
(
api
.
material
.
bump_map
,
constants
.
BUMP_MAP
,
constants
.
BUMP_SCALE
,
api
.
material
.
bump_scale
),
...
...
@@ -76,8 +83,9 @@ class Material(base_classes.BaseNode):
for
func
,
map_key
,
scale_key
,
scale_func
in
mapping
:
map_node
=
func
(
self
.
node
)
if
not
map_node
:
continue
logger
.
info
(
'Found map node %s for %s'
,
map_node
,
map_key
)
if
not
map_node
:
continue
logger
.
info
(
"Found map node %s for %s"
,
map_node
,
map_key
)
tex_inst
=
self
.
scene
.
texture
(
map_node
.
name
)
self
[
map_key
]
=
tex_inst
[
constants
.
UUID
]
self
[
map_key
]
=
tex_inst
[
constants
.
UUID
]
self
[
scale_key
]
=
scale_func
(
self
.
node
)
utils/exporters/blender/addons/io_three/exporter/object.py
浏览文件 @
51fdb4c7
...
...
@@ -3,18 +3,19 @@ from . import base_classes, api
class
Object
(
base_classes
.
BaseNode
):
"""Class that wraps an object node"""
def
__init__
(
self
,
node
,
parent
=
None
,
type
=
None
):
logger
.
debug
(
'Object().__init__(%s)'
,
node
)
logger
.
debug
(
"Object().__init__(%s)"
,
node
)
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
=
parent
,
type
=
type
)
if
self
.
node
:
self
.
_
_
node_setup
()
self
.
_node_setup
()
else
:
self
.
_
_
root_setup
()
self
.
_root_setup
()
def
__init_camera
(
self
):
logger
.
debug
(
'Object().__init_camera()'
)
def
_init_camera
(
self
):
"""Initialize camera attributes"""
logger
.
debug
(
"Object()._init_camera()"
)
self
[
constants
.
FAR
]
=
api
.
camera
.
far
(
self
.
node
)
self
[
constants
.
NEAR
]
=
api
.
camera
.
near
(
self
.
node
)
...
...
@@ -29,29 +30,32 @@ class Object(base_classes.BaseNode):
#@TODO: need more light attributes. Some may have to come from
# custom blender attributes.
def
__init_light
(
self
):
logger
.
debug
(
'Object().__init_light()'
)
def
_init_light
(
self
):
"""Initialize light attributes"""
logger
.
debug
(
"Object()._init_light()"
)
self
[
constants
.
COLOR
]
=
api
.
light
.
color
(
self
.
node
)
self
[
constants
.
INTENSITY
]
=
api
.
light
.
intensity
(
self
.
node
)
if
self
[
constants
.
TYPE
]
!=
constants
.
DIRECTIONAL_LIGHT
:
self
[
constants
.
DISTANCE
]
=
api
.
light
.
distance
(
self
.
node
)
if
self
[
constants
.
TYPE
]
==
constants
.
SPOT_LIGHT
:
self
[
constants
.
ANGLE
]
=
api
.
light
.
angle
(
self
.
node
)
def
__init_mesh
(
self
):
logger
.
debug
(
'Object().__init_mesh()'
)
def
_init_mesh
(
self
):
"""Initialize mesh attributes"""
logger
.
debug
(
"Object()._init_mesh()"
)
mesh
=
api
.
object
.
mesh
(
self
.
node
,
self
.
options
)
node
=
self
.
scene
.
geometry
(
mesh
)
if
node
:
self
[
constants
.
GEOMETRY
]
=
node
[
constants
.
UUID
]
else
:
msg
=
'Could not find Geometry() node for %s'
msg
=
"Could not find Geometry() node for %s"
logger
.
error
(
msg
,
self
.
node
)
def
__node_setup
(
self
):
logger
.
debug
(
'Object().__node_setup()'
)
def
_node_setup
(
self
):
"""Parse common node attributes of all objects"""
logger
.
debug
(
"Object()._node_setup()"
)
self
[
constants
.
NAME
]
=
api
.
object
.
name
(
self
.
node
)
self
[
constants
.
POSITION
]
=
api
.
object
.
position
(
...
...
@@ -68,49 +72,52 @@ class Object(base_classes.BaseNode):
self
[
constants
.
TYPE
]
=
api
.
object
.
node_type
(
self
.
node
)
if
self
.
options
.
get
(
constants
.
MATERIALS
):
logger
.
info
(
'Parsing materials for %s'
,
self
.
node
)
logger
.
info
(
"Parsing materials for %s"
,
self
.
node
)
material_name
=
api
.
object
.
material
(
self
.
node
)
if
material_name
:
logger
.
info
(
'Material found %s'
,
material_name
)
logger
.
info
(
"Material found %s"
,
material_name
)
material_inst
=
self
.
scene
.
material
(
material_name
)
self
[
constants
.
MATERIAL
]
=
material_inst
[
constants
.
UUID
]
else
:
logger
.
info
(
'%s has no materials'
,
self
.
node
)
logger
.
info
(
"%s has no materials"
,
self
.
node
)
casts_shadow
=
(
constants
.
MESH
,
constants
.
DIRECTIONAL_LIGHT
,
constants
.
SPOT_LIGHT
)
casts_shadow
=
(
constants
.
MESH
,
constants
.
DIRECTIONAL_LIGHT
,
constants
.
SPOT_LIGHT
)
if
self
[
constants
.
TYPE
]
in
casts_shadow
:
logger
.
info
(
'Querying shadow casting for %s'
,
self
.
node
)
logger
.
info
(
"Querying shadow casting for %s"
,
self
.
node
)
self
[
constants
.
CAST_SHADOW
]
=
\
api
.
object
.
cast_shadow
(
self
.
node
)
if
self
[
constants
.
TYPE
]
==
constants
.
MESH
:
logger
.
info
(
'Querying shadow receive for %s'
,
self
.
node
)
logger
.
info
(
"Querying shadow receive for %s"
,
self
.
node
)
self
[
constants
.
RECEIVE_SHADOW
]
=
\
api
.
object
.
receive_shadow
(
self
.
node
)
camera
=
(
constants
.
PERSPECTIVE_CAMERA
,
constants
.
ORTHOGRAPHIC_CAMERA
)
constants
.
ORTHOGRAPHIC_CAMERA
)
lights
=
(
constants
.
AMBIENT_LIGHT
,
constants
.
DIRECTIONAL_LIGHT
,
constants
.
AREA_LIGHT
,
constants
.
POINT_LIGHT
,
constants
.
SPOT_LIGHT
,
constants
.
HEMISPHERE_LIGHT
)
lights
=
(
constants
.
AMBIENT_LIGHT
,
constants
.
DIRECTIONAL_LIGHT
,
constants
.
AREA_LIGHT
,
constants
.
POINT_LIGHT
,
constants
.
SPOT_LIGHT
,
constants
.
HEMISPHERE_LIGHT
)
if
self
[
constants
.
TYPE
]
==
constants
.
MESH
:
self
.
_
_
init_mesh
()
self
.
_init_mesh
()
elif
self
[
constants
.
TYPE
]
in
camera
:
self
.
_
_
init_camera
()
self
.
_init_camera
()
elif
self
[
constants
.
TYPE
]
in
lights
:
self
.
_
_
init_light
()
self
.
_init_light
()
#
for child in api.object.children(self.node, self.scene.valid_types):
#
if not self.get(constants.CHILDREN):
#
self[constants.CHILDREN] = [Object(child, parent=self)]
#
else:
#
self[constants.CHILDREN].append(Object(child, parent=self))
for
child
in
api
.
object
.
children
(
self
.
node
,
self
.
scene
.
valid_types
):
if
not
self
.
get
(
constants
.
CHILDREN
):
self
[
constants
.
CHILDREN
]
=
[
Object
(
child
,
parent
=
self
)]
else
:
self
[
constants
.
CHILDREN
].
append
(
Object
(
child
,
parent
=
self
))
def
__root_setup
(
self
):
logger
.
debug
(
'Object().__root_setup()'
)
self
[
constants
.
MATRIX
]
=
[
1
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
1
]
def
_root_setup
(
self
):
"""Applies to a root/scene object"""
logger
.
debug
(
"Object()._root_setup()"
)
self
[
constants
.
MATRIX
]
=
[
1
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
1
]
utils/exporters/blender/addons/io_three/exporter/scene.py
浏览文件 @
51fdb4c7
...
...
@@ -4,14 +4,16 @@ from . import (
base_classes
,
texture
,
material
,
geometry
,
object
,
geometry
,
object
as
object_
,
utilities
,
io
,
api
)
class
Scene
(
base_classes
.
BaseScene
):
"""Class that handles the contruction of a Three scene"""
_defaults
=
{
constants
.
METADATA
:
constants
.
DEFAULT_METADATA
.
copy
(),
constants
.
GEOMETRIES
:
[],
...
...
@@ -21,7 +23,7 @@ class Scene(base_classes.BaseScene):
}
def
__init__
(
self
,
filepath
,
options
=
None
):
logger
.
debug
(
'Scene().__init__(%s, %s)'
,
filepath
,
options
)
logger
.
debug
(
"Scene().__init__(%s, %s)"
,
filepath
,
options
)
base_classes
.
BaseScene
.
__init__
(
self
,
filepath
,
options
or
{})
source_file
=
api
.
scene_name
()
...
...
@@ -30,73 +32,107 @@ class Scene(base_classes.BaseScene):
@
property
def
valid_types
(
self
):
"""
:return: list of valid node types
"""
valid_types
=
[
api
.
constants
.
MESH
]
if
self
.
options
.
get
(
constants
.
CAMERAS
):
logger
.
info
(
'Adding cameras to valid object types'
)
logger
.
info
(
"Adding cameras to valid object types"
)
valid_types
.
append
(
api
.
constants
.
CAMERA
)
if
self
.
options
.
get
(
constants
.
LIGHTS
):
logger
.
info
(
'Adding lights to valid object types'
)
logger
.
info
(
"Adding lights to valid object types"
)
valid_types
.
append
(
api
.
constants
.
LAMP
)
return
valid_types
def
geometry
(
self
,
arg
):
logger
.
debug
(
'Scene().geometry(%s)'
,
arg
)
return
self
.
_find_node
(
arg
,
self
[
constants
.
GEOMETRIES
])
def
geometry
(
self
,
value
):
"""Find a geometry node that matches either a name
or uuid value.
:param value: name or uuid
:type value: str
"""
logger
.
debug
(
"Scene().geometry(%s)"
,
value
)
return
_find_node
(
value
,
self
[
constants
.
GEOMETRIES
])
def
image
(
self
,
value
):
"""Find a image node that matches either a name
or uuid value.
def
image
(
self
,
arg
):
logger
.
debug
(
'Scene().image%s)'
,
arg
)
return
self
.
_find_node
(
arg
,
self
[
constants
.
IMAGES
])
:param value: name or uuid
:type value: str
def
material
(
self
,
arg
):
logger
.
debug
(
'Scene().material(%s)'
,
arg
)
return
self
.
_find_node
(
arg
,
self
[
constants
.
MATERIALS
])
"""
logger
.
debug
(
"Scene().image%s)"
,
value
)
return
_find_node
(
value
,
self
[
constants
.
IMAGES
])
def
material
(
self
,
value
):
"""Find a material node that matches either a name
or uuid value.
:param value: name or uuid
:type value: str
"""
logger
.
debug
(
"Scene().material(%s)"
,
value
)
return
_find_node
(
value
,
self
[
constants
.
MATERIALS
])
def
parse
(
self
):
logger
.
debug
(
'Scene().parse()'
)
"""Execute the parsing of the scene"""
logger
.
debug
(
"Scene().parse()"
)
if
self
.
options
.
get
(
constants
.
MAPS
):
self
.
_
_
parse_textures
()
self
.
_parse_textures
()
if
self
.
options
.
get
(
constants
.
MATERIALS
):
self
.
__parse_materials
()
self
.
_parse_materials
()
self
.
_parse_geometries
()
self
.
_parse_objects
()
def
texture
(
self
,
value
):
"""Find a texture node that matches either a name
or uuid value.
self
.
__parse_geometries
()
self
.
__parse_objects
()
:param value: name or uuid
:type value: str
def
texture
(
self
,
arg
):
logger
.
debug
(
'Scene().texture(%s)'
,
arg
)
return
self
.
_find_node
(
arg
,
self
[
constants
.
TEXTURES
])
"""
logger
.
debug
(
"Scene().texture(%s)"
,
value
)
return
_find_node
(
value
,
self
[
constants
.
TEXTURES
])
def
write
(
self
):
logger
.
debug
(
'Scene().write()'
)
"""Write the parsed scene to disk."""
logger
.
debug
(
"Scene().write()"
)
data
=
{}
embed_anim
=
self
.
options
.
get
(
constants
.
EMBED_ANIMATION
,
True
)
embed
=
self
.
options
[
constants
.
EMBED_GEOMETRY
]
embed
=
self
.
options
.
get
(
constants
.
EMBED_GEOMETRY
,
True
)
compression
=
self
.
options
.
get
(
constants
.
COMPRESSION
)
extension
=
constants
.
EXTENSIONS
.
get
(
compression
,
extension
=
constants
.
EXTENSIONS
.
get
(
compression
,
constants
.
EXTENSIONS
[
constants
.
JSON
])
export_dir
=
os
.
path
.
dirname
(
self
.
filepath
)
for
key
,
value
in
self
.
items
():
if
key
==
constants
.
GEOMETRIES
:
geometries
=
[]
for
geom
etry
in
value
:
for
geom
in
value
:
if
not
embed_anim
:
geom
etry
.
write_animation
(
export_dir
)
geom
.
write_animation
(
export_dir
)
geom_data
=
geom
.
copy
()
if
embed
:
for
each
in
value
:
geometries
.
append
(
each
.
copy
())
geometries
.
append
(
geom_data
)
continue
geom_data
=
geometry
.
copy
()
geo_type
=
geom_data
[
constants
.
TYPE
].
lower
()
if
geo_type
==
constants
.
GEOMETRY
.
lower
():
geom_data
.
pop
(
constants
.
DATA
)
...
...
@@ -104,10 +140,10 @@ class Scene(base_classes.BaseScene):
geom_data
.
pop
(
constants
.
ATTRIBUTES
)
geom_data
.
pop
(
constants
.
METADATA
)
url
=
'geometry.%s%s'
%
(
geom
etry
.
node
,
extension
)
url
=
'geometry.%s%s'
%
(
geom
.
node
,
extension
)
geometry_file
=
os
.
path
.
join
(
export_dir
,
url
)
geom
etry
.
write
(
filepath
=
geometry_file
)
geom
.
write
(
filepath
=
geometry_file
)
geom_data
[
constants
.
URL
]
=
os
.
path
.
basename
(
url
)
geometries
.
append
(
geom_data
)
...
...
@@ -124,20 +160,12 @@ class Scene(base_classes.BaseScene):
if
self
.
options
.
get
(
constants
.
COPY_TEXTURES
):
for
geo
in
self
[
constants
.
GEOMETRIES
]:
logger
.
info
(
'Copying textures from %s'
,
geo
.
node
)
logger
.
info
(
"Copying textures from %s"
,
geo
.
node
)
geo
.
copy_textures
()
def
_find_node
(
self
,
arg
,
manifest
):
for
index
in
manifest
:
uuid
=
index
.
get
(
constants
.
UUID
)
==
arg
name
=
index
.
node
==
arg
if
uuid
or
name
:
return
index
else
:
logger
.
debug
(
'No matching node for %s'
,
arg
)
def
__parse_geometries
(
self
):
logger
.
debug
(
'Scene().__parse_geometries()'
)
def
_parse_geometries
(
self
):
"""Locate all geometry nodes and parse them"""
logger
.
debug
(
"Scene()._parse_geometries()"
)
# this is an important step. please refer to the doc string
# on the function for more information
...
...
@@ -146,47 +174,75 @@ class Scene(base_classes.BaseScene):
# now iterate over all the extracted mesh nodes and parse each one
for
mesh
in
api
.
object
.
extracted_meshes
():
logger
.
info
(
'Parsing geometry %s'
,
mesh
)
logger
.
info
(
"Parsing geometry %s"
,
mesh
)
geo
=
geometry
.
Geometry
(
mesh
,
self
)
geo
.
parse
()
geometries
.
append
(
geo
)
logger
.
info
(
'Added %d geometry nodes'
,
len
(
geometries
))
logger
.
info
(
"Added %d geometry nodes"
,
len
(
geometries
))
self
[
constants
.
GEOMETRIES
]
=
geometries
def
__parse_materials
(
self
):
logger
.
debug
(
'Scene().__parse_materials()'
)
def
_parse_materials
(
self
):
"""Locate all non-orphaned materials and parse them"""
logger
.
debug
(
"Scene()._parse_materials()"
)
materials
=
[]
for
material_name
in
api
.
material
.
used_materials
():
logger
.
info
(
'Parsing material %s'
,
material_name
)
materials
.
append
(
material
.
Material
(
material_name
,
parent
=
self
))
logger
.
info
(
"Parsing material %s"
,
material_name
)
materials
.
append
(
material
.
Material
(
material_name
,
parent
=
self
))
logger
.
info
(
'Added %d material nodes'
,
len
(
materials
))
logger
.
info
(
"Added %d material nodes"
,
len
(
materials
))
self
[
constants
.
MATERIALS
]
=
materials
def
__parse_objects
(
self
):
logger
.
debug
(
'Scene().__parse_objects()'
)
self
[
constants
.
OBJECT
]
=
object
.
Object
(
None
,
parent
=
self
)
def
_parse_objects
(
self
):
"""Locate all valid objects in the scene and parse them"""
logger
.
debug
(
"Scene()._parse_objects()"
)
try
:
scene_name
=
self
[
constants
.
METADATA
][
constants
.
SOURCE_FILE
]
except
KeyError
:
scene_name
=
constants
.
SCENE
self
[
constants
.
OBJECT
]
=
object_
.
Object
(
None
,
parent
=
self
)
self
[
constants
.
OBJECT
][
constants
.
TYPE
]
=
constants
.
SCENE
.
title
()
self
[
constants
.
UUID
]
=
utilities
.
id_from_name
(
scene_name
)
objects
=
[]
for
node
in
api
.
object
.
nod
es
(
self
.
valid_types
,
self
.
options
):
logger
.
info
(
'Parsing object %s'
,
node
)
obj
=
object
.
Object
(
node
,
parent
=
self
[
constants
.
OBJECT
])
objects
=
[]
for
node
in
api
.
object
.
assembli
es
(
self
.
valid_types
,
self
.
options
):
logger
.
info
(
"Parsing object %s"
,
node
)
obj
=
object
_
.
Object
(
node
,
parent
=
self
[
constants
.
OBJECT
])
objects
.
append
(
obj
)
logger
.
info
(
'Added %d object nodes'
,
len
(
objects
))
logger
.
info
(
"Added %d object nodes"
,
len
(
objects
))
self
[
constants
.
OBJECT
][
constants
.
CHILDREN
]
=
objects
def
__parse_textures
(
self
):
logger
.
debug
(
'Scene().__parse_textures()'
)
def
_parse_textures
(
self
):
"""Locate all non-orphaned textures and parse them"""
logger
.
debug
(
"Scene()._parse_textures()"
)
textures
=
[]
for
texture_name
in
api
.
texture
.
textures
():
logger
.
info
(
'Parsing texture %s'
,
texture_name
)
logger
.
info
(
"Parsing texture %s"
,
texture_name
)
tex_inst
=
texture
.
Texture
(
texture_name
,
self
)
textures
.
append
(
tex_inst
)
logger
.
info
(
'Added %d texture nodes'
,
len
(
textures
))
logger
.
info
(
"Added %d texture nodes"
,
len
(
textures
))
self
[
constants
.
TEXTURES
]
=
textures
def
_find_node
(
value
,
manifest
):
"""Find a node that matches either a name
or uuid value.
:param value: name or uuid
:param manifest: manifest of nodes to search
:type value: str
:type manifest: list
"""
for
index
in
manifest
:
uuid
=
index
.
get
(
constants
.
UUID
)
==
value
name
=
index
.
node
==
value
if
uuid
or
name
:
return
index
else
:
logger
.
debug
(
"No matching node for %s"
,
value
)
utils/exporters/blender/addons/io_three/exporter/texture.py
浏览文件 @
51fdb4c7
...
...
@@ -3,8 +3,9 @@ from . import base_classes, image, api
class
Texture
(
base_classes
.
BaseNode
):
"""Class that wraps a texture node"""
def
__init__
(
self
,
node
,
parent
):
logger
.
debug
(
'Texture().__init__(%s)'
,
node
)
logger
.
debug
(
"Texture().__init__(%s)"
,
node
)
base_classes
.
BaseNode
.
__init__
(
self
,
node
,
parent
,
constants
.
TEXTURE
)
img_inst
=
self
.
scene
.
image
(
api
.
texture
.
file_name
(
self
.
node
))
...
...
@@ -29,4 +30,10 @@ class Texture(base_classes.BaseNode):
@
property
def
image
(
self
):
"""
:return: the image object of the current texture
:rtype: image.Image
"""
return
self
.
scene
.
image
(
self
[
constants
.
IMAGE
])
utils/exporters/blender/addons/io_three/exporter/utilities.py
浏览文件 @
51fdb4c7
...
...
@@ -8,9 +8,15 @@ ROUND = constants.DEFAULT_PRECISION
def
bit_mask
(
flags
):
"""Generate a bit mask.
:type flags: dict
:return: int
"""
bit
=
0
true
=
lambda
x
,
y
:
(
x
|
(
1
<<
y
))
false
=
lambda
x
,
y
:
(
x
&
(
~
(
1
<<
y
)))
true
=
lambda
x
,
y
:
(
x
|
(
1
<<
y
))
false
=
lambda
x
,
y
:
(
x
&
(
~
(
1
<<
y
)))
for
mask
,
position
in
constants
.
MASK
.
items
():
func
=
true
if
flags
.
get
(
mask
)
else
false
...
...
@@ -20,16 +26,43 @@ def bit_mask(flags):
def
hash
(
value
):
"""Generate a hash from a given value
:param value:
:rtype: str
"""
hash_
=
hashlib
.
md5
()
hash_
.
update
(
repr
(
value
).
encode
(
'utf8'
))
return
hash_
.
hexdigest
()
def
id
():
"""Generate a random UUID
:rtype: str
"""
return
str
(
uuid
.
uuid4
()).
upper
()
def
id_from_name
(
name
):
"""Generate a UUID using a name as the namespace
:type name: str
:rtype: str
"""
return
str
(
uuid
.
uuid3
(
uuid
.
NAMESPACE_DNS
,
name
)).
upper
()
def
rgb2int
(
rgb
):
"""Convert a given rgb value to an integer
:type rgb: list|tuple
:rtype: int
"""
is_tuple
=
isinstance
(
rgb
,
tuple
)
rgb
=
list
(
rgb
)
if
is_tuple
else
rgb
...
...
@@ -38,6 +71,15 @@ def rgb2int(rgb):
def
round_off
(
value
,
ndigits
=
ROUND
):
"""Round off values to specified limit
:param value: value(s) to round off
:param ndigits: limit (Default = ROUND)
:type value: float|list|tuple
:return: the same data type that was passed
:rtype: float|list|tuple
"""
is_tuple
=
isinstance
(
value
,
tuple
)
is_list
=
isinstance
(
value
,
list
)
...
...
@@ -55,10 +97,17 @@ def round_off(value, ndigits=ROUND):
def
rounding
(
options
):
round_off
=
options
.
get
(
constants
.
ENABLE_PRECISION
)
if
round_off
:
"""By evaluation the options determine if precision was
enabled and what the value is
:type options: dict
:rtype: bool, int
"""
round_off_
=
options
.
get
(
constants
.
ENABLE_PRECISION
)
if
round_off_
:
round_val
=
options
[
constants
.
PRECISION
]
else
:
round_val
=
None
return
(
round_off
,
round_val
)
return
(
round_off
_
,
round_val
)
utils/exporters/blender/addons/io_three/logger.py
浏览文件 @
51fdb4c7
...
...
@@ -15,7 +15,14 @@ LEVELS = {
constants
.
CRITICAL
:
logging
.
CRITICAL
}
def
init
(
filename
,
level
=
constants
.
DEBUG
):
"""Initialize the logger.
:param filename: base name of the log file
:param level: logging level (Default = DEBUG)
"""
global
LOG_FILE
LOG_FILE
=
os
.
path
.
join
(
tempfile
.
gettempdir
(),
filename
)
with
open
(
LOG_FILE
,
'w'
):
...
...
utils/exporters/blender/tests/blend/anim.blend
浏览文件 @
51fdb4c7
无法预览此类型文件
utils/exporters/blender/tests/blend/scene_children.blend
0 → 100644
浏览文件 @
51fdb4c7
文件已添加
utils/exporters/blender/tests/scripts/js/review.js
浏览文件 @
51fdb4c7
...
...
@@ -99,6 +99,10 @@ function loadObject( data ) {
camera
=
scene
.
children
[
i
];
var
container
=
document
.
getElementById
(
'
viewport
'
);
orbit
=
new
THREE
.
OrbitControls
(
camera
,
container
);
orbit
.
addEventListener
(
'
change
'
,
render
);
var
aspect
=
container
.
offsetWidth
/
container
.
offsetHeight
;
camera
.
aspect
=
aspect
;
camera
.
updateProjectionMatrix
();
...
...
@@ -131,14 +135,14 @@ function loadGeometry( data, url ) {
var
material
=
new
THREE
.
MeshFaceMaterial
(
data
.
materials
);
var
mesh
;
if
(
data
.
geometry
.
animation
!==
undefined
)
{
if
(
data
.
geometry
.
animation
s
!==
undefined
&&
data
.
geometry
.
animations
.
length
>
0
)
{
console
.
log
(
'
loading animation
'
);
data
.
materials
[
0
].
skinning
=
true
;
mesh
=
new
THREE
.
SkinnedMesh
(
data
.
geometry
,
material
,
false
);
mesh
=
new
THREE
.
SkinnedMesh
(
data
.
geometry
,
material
,
false
);
var
name
=
data
.
geometry
.
animation
.
name
;
animation
=
new
THREE
.
Animation
(
mesh
,
data
.
geometry
.
animation
);
var
name
=
data
.
geometry
.
animation
s
[
0
]
.
name
;
animation
=
new
THREE
.
Animation
(
mesh
,
data
.
geometry
.
animation
s
[
0
]
);
}
else
{
...
...
utils/exporters/blender/tests/scripts/test_geometry_normals.bash
浏览文件 @
51fdb4c7
...
...
@@ -4,5 +4,5 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source
"
$DIR
/setup_test_env.bash"
blender
--background
$BLEND
/torusA.blend
--python
$PYSCRIPT
--
\
$JSON
--vertices
--faces
--normals
$JSON
--vertices
--faces
--normals
--indent
makereview
$@
--tag
$(
tagname
)
utils/exporters/blender/tests/scripts/test_scene_children.bash
0 → 100755
浏览文件 @
51fdb4c7
#!/bin/bash
DIR
=
"
$(
cd
"
$(
dirname
"
${
BASH_SOURCE
[0]
}
"
)
"
&&
pwd
)
"
source
"
$DIR
/setup_test_env.bash"
blender
--background
$BLEND
/scene_children.blend
\
--python
$PYSCRIPT
--
$JSON
--vertices
--faces
--scene
\
--cameras
--materials
--embedGeometry
--lights
--cameras
makereview
$@
--tag
$(
tagname
)
utils/exporters/blender/tests/scripts/test_scene_instancing.bash
浏览文件 @
51fdb4c7
...
...
@@ -5,5 +5,5 @@ source "$DIR/setup_test_env.bash"
blender
--background
$BLEND
/scene_instancing.blend
--python
$PYSCRIPT
--
\
$JSON
--vertices
--faces
--scene
--materials
--enablePrecision
\
--precision
4
--embedGeometry
--precision
4
--embedGeometry
--indent
makereview
$@
--tag
$(
tagname
)
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录