Grease pencil essentials.
This commit is contained in:
parent
e7ba7c30ba
commit
a2a7cf4a18
|
@ -237,6 +237,13 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
os.makedirs(mesh_fp)
|
||||
return mesh_fp + object_id + '.arm'
|
||||
|
||||
def get_greasepencils_file_path(self, object_id):
|
||||
index = self.filepath.rfind('/')
|
||||
gp_fp = self.filepath[:(index+1)] + 'greasepencils/'
|
||||
if not os.path.exists(gp_fp):
|
||||
os.makedirs(gp_fp)
|
||||
return gp_fp + object_id + '.arm'
|
||||
|
||||
@staticmethod
|
||||
def GetBObjectType(bobject):
|
||||
if bobject.type == "MESH":
|
||||
|
@ -1325,7 +1332,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
if (not bone.parent):
|
||||
self.ProcessBone(bone)
|
||||
|
||||
if bobject.type != 'MESH' or self.object_has_instanced_children(bobject) == False:
|
||||
if bobject.type != 'MESH' or bobject.instanced_children == False:
|
||||
for subbobject in bobject.children:
|
||||
self.ProcessBObject(subbobject)
|
||||
|
||||
|
@ -1557,7 +1564,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
if not hasattr(o, 'children'):
|
||||
o['children'] = []
|
||||
|
||||
if bobject.type != 'MESH' or self.object_has_instanced_children(bobject) == False:
|
||||
if bobject.type != 'MESH' or bobject.instanced_children == False:
|
||||
for subbobject in bobject.children:
|
||||
if (subbobject.parent_type != "BONE"):
|
||||
self.ExportObject(subbobject, scene, None, o)
|
||||
|
@ -1737,7 +1744,11 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
mesh_obj = {}
|
||||
mesh_obj['mesh_datas'] = [o]
|
||||
utils.write_arm(fp, mesh_obj)
|
||||
self.object_set_mesh_cached(bobject, True)
|
||||
|
||||
bobject.data.mesh_cached = True
|
||||
if bobject.type != 'FONT':
|
||||
bobject.data.mesh_cached_verts = len(bobject.data.vertices)
|
||||
bobject.data.mesh_cached_edges = len(bobject.data.edges)
|
||||
else:
|
||||
self.output['mesh_datas'].append(o)
|
||||
|
||||
|
@ -2239,7 +2250,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
o['attenuation'] = objref.attenuation
|
||||
self.output['speaker_datas'].append(o)
|
||||
|
||||
def ExportMaterials(self):
|
||||
def export_materials(self):
|
||||
# This function exports all of the materials used in the scene
|
||||
for materialRef in self.materialArray.items():
|
||||
material = materialRef[0]
|
||||
|
@ -2302,7 +2313,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
self.finalize_shader(o, defs, ArmoryExporter.renderpath_passes)
|
||||
self.output['material_datas'].append(o)
|
||||
|
||||
def ExportParticleSystems(self):
|
||||
def export_particle_systems(self):
|
||||
for particleRef in self.particleSystemArray.items():
|
||||
o = {}
|
||||
psettings = particleRef[0]
|
||||
|
@ -2318,19 +2329,40 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
o['factor_random'] = psettings.factor_random
|
||||
self.output['particle_datas'].append(o)
|
||||
|
||||
def ExportWorlds(self):
|
||||
def export_worlds(self):
|
||||
# for worldRef in self.worldArray.items():
|
||||
# for worldRef in bpy.data.worlds:
|
||||
worldRef = self.scene.world
|
||||
if worldRef != None:
|
||||
o = {}
|
||||
# w = worldRef[0]
|
||||
w = worldRef
|
||||
# o.id = worldRef[1]["structName"]
|
||||
o['name'] = w.name
|
||||
self.cb_export_world(w, o)
|
||||
self.output['world_datas'].append(o)
|
||||
|
||||
def export_grease_pencils(self):
|
||||
gpRef = self.scene.grease_pencil
|
||||
if gpRef == None:
|
||||
return
|
||||
|
||||
# ArmoryExporter.option_mesh_per_file # Currently always exports to separate file
|
||||
fp = self.get_greasepencils_file_path('greasepencil_' + gpRef.name)
|
||||
assets.add(fp)
|
||||
self.output['grease_pencil_ref'] = 'greasepencil_' + gpRef.name + '/' + gpRef.name
|
||||
|
||||
assets.add_shader_data('build/compiled/ShaderDatas/grease_pencil/grease_pencil.arm')
|
||||
assets.add_shader('build/compiled/Shaders/grease_pencil/grease_pencil.frag.glsl')
|
||||
assets.add_shader('build/compiled/Shaders/grease_pencil/grease_pencil.vert.glsl')
|
||||
|
||||
if gpRef.data_cached == True and os.path.exists(fp):
|
||||
return
|
||||
|
||||
gpo = self.cb_export_grease_pencil(gpRef)
|
||||
gp_obj = {}
|
||||
gp_obj['grease_pencil_datas'] = [gpo]
|
||||
utils.write_arm(fp, gp_obj)
|
||||
gpRef.data_cached = True
|
||||
|
||||
def ExportObjects(self, scene):
|
||||
if not ArmoryExporter.option_mesh_only:
|
||||
self.output['lamp_datas'] = []
|
||||
|
@ -2409,13 +2441,16 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
print('Armory Warning: No camera found in active scene')
|
||||
|
||||
self.output['material_datas'] = []
|
||||
self.ExportMaterials()
|
||||
self.export_materials()
|
||||
|
||||
self.output['particle_datas'] = []
|
||||
self.ExportParticleSystems()
|
||||
self.export_particle_systems()
|
||||
|
||||
self.output['world_datas'] = []
|
||||
self.ExportWorlds()
|
||||
self.export_worlds()
|
||||
|
||||
self.output['grease_pencil_datas'] = []
|
||||
self.export_grease_pencils()
|
||||
|
||||
if self.scene.world != None:
|
||||
self.output['world_ref'] = self.scene.world.name
|
||||
|
@ -2453,9 +2488,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
return {'FINISHED'}
|
||||
|
||||
# Callbacks
|
||||
def object_has_instanced_children(self, bobject):
|
||||
return bobject.instanced_children
|
||||
|
||||
def object_is_mesh_cached(self, bobject):
|
||||
if bobject.type == 'FONT': # No verts for font
|
||||
return bobject.data.mesh_cached
|
||||
|
@ -2465,12 +2497,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
return False
|
||||
return bobject.data.mesh_cached
|
||||
|
||||
def object_set_mesh_cached(self, bobject, b):
|
||||
bobject.data.mesh_cached = b
|
||||
if b and bobject.type != 'FONT':
|
||||
bobject.data.mesh_cached_verts = len(bobject.data.vertices)
|
||||
bobject.data.mesh_cached_edges = len(bobject.data.edges)
|
||||
|
||||
def get_export_tangents(self, mesh):
|
||||
for m in mesh.materials:
|
||||
if m.export_tangents == True:
|
||||
|
@ -2906,8 +2932,8 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
defs = bpy.data.worlds['Arm'].world_defs
|
||||
generate_irradiance = True #'_EnvTex' in defs or '_EnvSky' in defs or '_EnvCon' in defs
|
||||
disable_hdr = world.world_envtex_name.endswith('.jpg')
|
||||
irrtex = world.world_envtex_name.rsplit('.', 1)[0]
|
||||
radtex = irrtex
|
||||
radtex = world.world_envtex_name.rsplit('.', 1)[0]
|
||||
irrsharmonics = world.name
|
||||
|
||||
# Radiance
|
||||
if '_EnvTex' in defs:
|
||||
|
@ -2918,7 +2944,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
|
||||
num_mips = world.world_envtex_num_mips
|
||||
strength = world.world_envtex_strength
|
||||
po = self.make_probe(world.name, irrtex, radtex, num_mips, strength, 1.0, [0, 0, 0], [0, 0, 0], world_generate_radiance, generate_irradiance, disable_hdr)
|
||||
po = self.make_probe(world.name, irrsharmonics, radtex, num_mips, strength, 1.0, [0, 0, 0], [0, 0, 0], world_generate_radiance, generate_irradiance, disable_hdr)
|
||||
o['probes'].append(po)
|
||||
|
||||
if '_EnvSky' in defs:
|
||||
|
@ -2947,7 +2973,108 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
po = self.make_probe(cam.name, base_name, base_name, cam.probe_num_mips, cam.probe_strength, cam.probe_blending, volume, volume_center, generate_radiance, generate_irradiance, disable_hdr)
|
||||
o['probes'].append(po)
|
||||
|
||||
def make_probe(self, id, irrtex, radtex, mipmaps, strength, blending, volume, volume_center, generate_radiance, generate_irradiance, disable_hdr):
|
||||
def cb_export_grease_pencil(self, gp):
|
||||
o = {}
|
||||
o['name'] = gp.name
|
||||
o['layers'] = []
|
||||
# originalFrame = self.scene.frame_current
|
||||
for layer in gp.layers:
|
||||
o['layers'].append(self.export_grease_pencil_layer(layer))
|
||||
# self.scene.frame_set(originalFrame)
|
||||
# o['palettes'] = []
|
||||
# for palette in gp.palettes:
|
||||
# o['palettes'].append(self.export_grease_pencil_palette(palette))
|
||||
o['shader'] = 'grease_pencil/grease_pencil'
|
||||
return o
|
||||
|
||||
def export_grease_pencil_layer(self, layer):
|
||||
lo = {}
|
||||
lo['name'] = layer.info
|
||||
lo['opacity'] = layer.opacity
|
||||
lo['frames'] = []
|
||||
for frame in layer.frames:
|
||||
if frame.frame_number > self.scene.frame_end:
|
||||
break
|
||||
# TODO: load GP frame data
|
||||
# self.scene.frame_set(frame.frame_number)
|
||||
lo['frames'].append(self.export_grease_pencil_frame(frame))
|
||||
return lo
|
||||
|
||||
def export_grease_pencil_frame(self, frame):
|
||||
va = []
|
||||
cola = []
|
||||
colfilla = []
|
||||
indices = []
|
||||
index_offset = 0
|
||||
for stroke in frame.strokes:
|
||||
for point in stroke.points:
|
||||
va.append(point.co[0])
|
||||
va.append(point.co[1])
|
||||
va.append(point.co[2])
|
||||
# TODO: store index to color pallete only, this is extremely wasteful
|
||||
if stroke.color != None:
|
||||
cola.append(stroke.color.color[0])
|
||||
cola.append(stroke.color.color[1])
|
||||
cola.append(stroke.color.color[2])
|
||||
cola.append(stroke.color.alpha)
|
||||
colfilla.append(stroke.color.fill_color[0])
|
||||
colfilla.append(stroke.color.fill_color[1])
|
||||
colfilla.append(stroke.color.fill_color[2])
|
||||
colfilla.append(stroke.color.fill_alpha)
|
||||
else:
|
||||
cola.append(0.0)
|
||||
cola.append(0.0)
|
||||
cola.append(0.0)
|
||||
cola.append(0.0)
|
||||
colfilla.append(0.0)
|
||||
colfilla.append(0.0)
|
||||
colfilla.append(0.0)
|
||||
colfilla.append(0.0)
|
||||
|
||||
for triangle in stroke.triangles:
|
||||
indices.append(triangle.v1 + index_offset)
|
||||
indices.append(triangle.v2 + index_offset)
|
||||
indices.append(triangle.v3 + index_offset)
|
||||
index_offset += len(stroke.points)
|
||||
fo = {}
|
||||
# TODO: merge into array of vertex arrays
|
||||
fo['vertex_array'] = {}
|
||||
fo['vertex_array']['attrib'] = 'pos'
|
||||
fo['vertex_array']['size'] = 3
|
||||
fo['vertex_array']['values'] = va
|
||||
fo['col_array'] = {}
|
||||
fo['col_array']['attrib'] = 'col'
|
||||
fo['col_array']['size'] = 4
|
||||
fo['col_array']['values'] = cola
|
||||
fo['colfill_array'] = {}
|
||||
fo['colfill_array']['attrib'] = 'colfill'
|
||||
fo['colfill_array']['size'] = 4
|
||||
fo['colfill_array']['values'] = colfilla
|
||||
fo['index_array'] = {}
|
||||
fo['index_array']['material'] = 0
|
||||
fo['index_array']['size'] = 3
|
||||
fo['index_array']['values'] = indices
|
||||
fo['frame_number'] = frame.frame_number
|
||||
return fo
|
||||
|
||||
def export_grease_pencil_palette(self, palette):
|
||||
po = {}
|
||||
po['name'] = palette.info
|
||||
po['colors'] = []
|
||||
for color in palette.colors:
|
||||
po['colors'].append(self.export_grease_pencil_palette_color(color))
|
||||
return po
|
||||
|
||||
def export_grease_pencil_palette_color(self, color):
|
||||
co = {}
|
||||
co['name'] = color.name
|
||||
co['color'] = [color.color[0], color.color[1], color.color[2]]
|
||||
co['alpha'] = color.alpha
|
||||
co['fill_color'] = [color.fill_color[0], color.fill_color[1], color.fill_color[2]]
|
||||
co['fill_alpha'] = color.fill_alpha
|
||||
return co
|
||||
|
||||
def make_probe(self, id, irrsharmonics, radtex, mipmaps, strength, blending, volume, volume_center, generate_radiance, generate_irradiance, disable_hdr):
|
||||
po = {}
|
||||
po['name'] = id
|
||||
if generate_radiance:
|
||||
|
@ -2958,7 +3085,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
po['radiance'] += '.hdr'
|
||||
po['radiance_mipmaps'] = mipmaps
|
||||
if generate_irradiance:
|
||||
po['irradiance'] = irrtex + '_irradiance'
|
||||
po['irradiance'] = irrsharmonics + '_irradiance'
|
||||
else:
|
||||
po['irradiance'] = '' # No irradiance data, fallback to default at runtime
|
||||
po['strength'] = strength
|
||||
|
|
|
@ -273,6 +273,9 @@ def make_draw_compositor(stage, node_group, node, with_fxaa=False):
|
|||
# assets.add(buildNodeTrees.assets_path + 'noise256.png')
|
||||
# assets.add_embedded_data('noise256.png')
|
||||
|
||||
def make_draw_grease_pencil(stage, node_group, node):
|
||||
stage['command'] = 'draw_grease_pencil'
|
||||
|
||||
def make_call_function(stage, node_group, node):
|
||||
stage['command'] = 'call_function'
|
||||
stage['params'].append(node.inputs[1].default_value)
|
||||
|
@ -535,6 +538,11 @@ def buildNode(stages, node, node_group):
|
|||
with_fxaa = node.bl_idname == 'DrawCompositorWithFXAANodeType'
|
||||
make_draw_compositor(stage, node_group, node, with_fxaa=with_fxaa)
|
||||
|
||||
elif node.bl_idname == 'DrawGreasePencilNodeType':
|
||||
stage = {}
|
||||
stage['params'] = []
|
||||
make_draw_grease_pencil(stage, node_group, node)
|
||||
|
||||
elif node.bl_idname == 'BranchFunctionNodeType':
|
||||
make_branch_function(stage, node_group, node)
|
||||
stages.append(stage)
|
||||
|
|
|
@ -640,6 +640,17 @@ class DrawCompositorWithFXAANode(Node, CGPipelineTreeNode):
|
|||
|
||||
self.outputs.new('NodeSocketShader', "Stage")
|
||||
|
||||
class DrawGreasePencilNode(Node, CGPipelineTreeNode):
|
||||
'''Draw grease pencil node'''
|
||||
bl_idname = 'DrawGreasePencilNodeType'
|
||||
bl_label = 'Draw Grease Pencil'
|
||||
bl_icon = 'SOUND'
|
||||
|
||||
def init(self, context):
|
||||
self.inputs.new('NodeSocketShader', "Stage")
|
||||
|
||||
self.outputs.new('NodeSocketShader', "Stage")
|
||||
|
||||
# Constant nodes
|
||||
class ScreenNode(Node, CGPipelineTreeNode):
|
||||
'''Reference screen dimensions node'''
|
||||
|
@ -713,6 +724,7 @@ node_categories = [
|
|||
NodeItem("DrawWorldNodeType"),
|
||||
NodeItem("DrawCompositorNodeType"),
|
||||
NodeItem("DrawCompositorWithFXAANodeType"),
|
||||
NodeItem("DrawGreasePencilNodeType"),
|
||||
]),
|
||||
MyTargetNodeCategory("TARGETNODES", "Target", items=[
|
||||
NodeItem("TargetNodeType"),
|
||||
|
|
|
@ -259,6 +259,8 @@ def initProperties():
|
|||
bpy.types.Mesh.dynamic_usage = bpy.props.BoolProperty(name="Dynamic Data Usage", description="Mesh data can change at runtime", default=False)
|
||||
bpy.types.Curve.mesh_cached = bpy.props.BoolProperty(name="Mesh Cached", description="No need to reexport curve data", default=False)
|
||||
bpy.types.Curve.dynamic_usage = bpy.props.BoolProperty(name="Dynamic Data Usage", description="Curve data can change at runtime", default=False)
|
||||
# For grease pencil
|
||||
bpy.types.GreasePencil.data_cached = bpy.props.BoolProperty(name="GP Cached", description="No need to reexport grease pencil data", default=False)
|
||||
# For armature
|
||||
bpy.types.Armature.armature_cached = bpy.props.BoolProperty(name="Armature Cached", description="No need to reexport armature data", default=False)
|
||||
# Actions
|
||||
|
@ -588,10 +590,11 @@ class ScenePropsPanel(bpy.types.Panel):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scn = bpy.context.scene
|
||||
if scn == None:
|
||||
scene = bpy.context.scene
|
||||
if scene == None:
|
||||
return
|
||||
layout.prop(scn, 'game_export')
|
||||
layout.prop(scene, 'game_export')
|
||||
layout.operator('arm.invalidate_gp_cache')
|
||||
|
||||
class ReimportPathsMenu(bpy.types.Menu):
|
||||
bl_label = "OK?"
|
||||
|
@ -628,6 +631,16 @@ class OBJECT_OT_INVALIDATECACHEButton(bpy.types.Operator):
|
|||
context.object.data.mesh_cached = False
|
||||
return{'FINISHED'}
|
||||
|
||||
class InvalidateGPCacheButton(bpy.types.Operator):
|
||||
'''Delete cached grease pencil data'''
|
||||
bl_idname = "arm.invalidate_gp_cache"
|
||||
bl_label = "Invalidate GP Cache"
|
||||
|
||||
def execute(self, context):
|
||||
if context.scene.grease_pencil != None:
|
||||
context.scene.grease_pencil.data_cached = False
|
||||
return{'FINISHED'}
|
||||
|
||||
# Menu in materials region
|
||||
class MatsPropsPanel(bpy.types.Panel):
|
||||
bl_label = "Armory Props"
|
||||
|
|
|
@ -24,9 +24,6 @@ let project = new Project('""" + bpy.data.worlds['Arm'].ArmProjectName + """');
|
|||
project.addSources('Sources');
|
||||
project.addShaders('Sources/Shaders/**');
|
||||
""")
|
||||
# project.addAssets('build/compiled/Assets/**');
|
||||
for file in assets.assets:
|
||||
f.write("project.addAssets('" + file + "');\n")
|
||||
|
||||
f.write(add_armory_library(sdk_path, 'armory'))
|
||||
f.write(add_armory_library(sdk_path, 'iron'))
|
||||
|
|
13
raw/grease_pencil/grease_pencil.frag.glsl
Normal file
13
raw/grease_pencil/grease_pencil.frag.glsl
Normal file
|
@ -0,0 +1,13 @@
|
|||
#version 450
|
||||
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
in vec4 color;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = color;
|
||||
}
|
54
raw/grease_pencil/grease_pencil.shader.json
Executable file
54
raw/grease_pencil/grease_pencil.shader.json
Executable file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"contexts": [
|
||||
{
|
||||
"name": "grease_pencil",
|
||||
"params": [
|
||||
{
|
||||
"name": "depth_write",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "compare_mode",
|
||||
"value": "less"
|
||||
},
|
||||
{
|
||||
"name": "cull_mode",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "blend_source",
|
||||
"value": "source_alpha"
|
||||
},
|
||||
{
|
||||
"name": "blend_destination",
|
||||
"value": "inverse_source_alpha"
|
||||
},
|
||||
{
|
||||
"name": "blend_operation",
|
||||
"value": "add"
|
||||
},
|
||||
{
|
||||
"name": "alpha_blend_source",
|
||||
"value": "source_alpha"
|
||||
},
|
||||
{
|
||||
"name": "alpha_blend_destination",
|
||||
"value": "inverse_source_alpha"
|
||||
},
|
||||
{
|
||||
"name": "alpha_blend_operation",
|
||||
"value": "add"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"name": "VP",
|
||||
"link": "_viewProjectionMatrix"
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
"vertex_shader": "grease_pencil.vert.glsl",
|
||||
"fragment_shader": "grease_pencil.frag.glsl"
|
||||
}
|
||||
]
|
||||
}
|
17
raw/grease_pencil/grease_pencil.vert.glsl
Normal file
17
raw/grease_pencil/grease_pencil.vert.glsl
Normal file
|
@ -0,0 +1,17 @@
|
|||
#version 450
|
||||
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
in vec3 pos;
|
||||
in vec4 col;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform mat4 VP;
|
||||
|
||||
void main() {
|
||||
color = col;
|
||||
gl_Position = VP * vec4(pos.xyz, 1.0);
|
||||
}
|
Loading…
Reference in a new issue