armory/blender/project.py

290 lines
7.9 KiB
Python
Raw Normal View History

2015-12-07 20:04:23 +01:00
import os
import sys
2015-10-30 13:23:09 +01:00
import shutil
import bpy
import platform
import json
from bpy.props import *
import subprocess
import atexit
import webbrowser
2015-12-07 21:05:27 +01:00
import write_data
2016-01-28 23:26:10 +01:00
import nodes_logic
2016-01-28 14:47:46 +01:00
import nodes_pipeline
2016-02-08 14:58:55 +01:00
import nodes_world
2016-01-11 13:07:44 +01:00
from armory import ArmoryExporter
2015-10-30 13:23:09 +01:00
def defaultSettings():
2016-02-08 17:28:05 +01:00
wrd = bpy.data.worlds[0]
wrd['CGVersion'] = "16.1.0"
wrd['CGProjectTarget'] = 0
wrd['CGProjectName'] = "cycles_game"
wrd['CGProjectPackage'] = "game"
wrd['CGProjectWidth'] = 1136
wrd['CGProjectHeight'] = 640
wrd['CGProjectScene'] = bpy.data.scenes[0].name
wrd['CGAA'] = 1
wrd['CGPhysics'] = 0
wrd['CGMinimize'] = (True)
# Make sure we are using cycles
if bpy.data.scenes[0].render.engine == 'BLENDER_RENDER':
for scene in bpy.data.scenes:
scene.render.engine = 'CYCLES'
2015-10-30 13:23:09 +01:00
# Store properties in the world object
def initWorldProperties():
2016-02-08 17:28:05 +01:00
bpy.types.World.CGVersion = StringProperty(name = "CGVersion")
bpy.types.World.CGProjectTarget = EnumProperty(
items = [('HTML5', 'HTML5', 'HTML5'),
('Windows', 'Windows', 'Windows'),
('OSX', 'OSX', 'OSX'),
('Linux', 'Linux', 'Linux'),
('iOS', 'iOS', 'iOS'),
('Android', 'Android', 'Android')],
name = "Target")
bpy.types.World.CGProjectName = StringProperty(name = "Name")
bpy.types.World.CGProjectPackage = StringProperty(name = "Package")
bpy.types.World.CGProjectWidth = IntProperty(name = "Width")
bpy.types.World.CGProjectHeight = IntProperty(name = "Height")
bpy.types.World.CGProjectScene = StringProperty(name = "Scene")
bpy.types.World.CGAA = EnumProperty(
items = [('Disabled', 'Disabled', 'Disabled'),
('16X', '16X', '16X')],
name = "Anti-aliasing")
bpy.types.World.CGPhysics = EnumProperty(
items = [('Disabled', 'Disabled', 'Disabled'),
('Bullet', 'Bullet', 'Bullet')],
name = "Physics")
bpy.types.World.CGMinimize = BoolProperty(name = "Minimize")
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Default settings
if not 'CGVersion' in bpy.data.worlds[0]:
defaultSettings()
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Use material nodes
for mat in bpy.data.materials:
bpy.ops.cycles.use_shading_nodes({"material":mat})
# Use world nodes
for wrd in bpy.data.worlds:
bpy.ops.cycles.use_shading_nodes({"world":wrd})
return
2015-10-30 13:23:09 +01:00
# Info panel play
def draw_play_item(self, context):
2016-02-08 17:28:05 +01:00
layout = self.layout
layout.operator("cg.play")
2015-10-30 13:23:09 +01:00
2016-01-17 22:31:31 +01:00
# Menu in render region
2015-10-30 13:23:09 +01:00
class ToolsPanel(bpy.types.Panel):
2016-02-08 17:28:05 +01:00
bl_label = "Cycles Game"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Info panel play
#bpy.types.INFO_HT_header.prepend(draw_play_item)
bpy.types.VIEW3D_HT_header.append(draw_play_item)
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
def draw(self, context):
layout = self.layout
wrd = bpy.data.worlds[0]
layout.prop(wrd, 'CGProjectName')
layout.prop(wrd, 'CGProjectPackage')
row = layout.row()
row.prop(wrd, 'CGProjectWidth')
row.prop(wrd, 'CGProjectHeight')
layout.prop_search(wrd, "CGProjectScene", bpy.data, "scenes", "Scene")
layout.prop(wrd, 'CGProjectTarget')
layout.operator("cg.build")
row = layout.row(align=True)
row.alignment = 'EXPAND'
row.operator("cg.folder")
row.operator("cg.clean")
layout.prop(wrd, 'CGAA')
layout.prop(wrd, 'CGPhysics')
layout.prop(wrd, 'CGMinimize')
2015-10-30 13:23:09 +01:00
class Object:
2016-02-08 17:28:05 +01:00
def to_JSON(self):
if bpy.data.worlds[0]['CGMinimize'] == True:
return json.dumps(self, default=lambda o: o.__dict__, separators=(',',':'))
else:
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Transform Blender data into game data
2015-10-30 13:23:09 +01:00
def exportGameData():
2016-02-08 17:28:05 +01:00
shader_references = []
asset_references = []
# Build nodes # TODO: only if needed
nodes_logic.buildNodeTrees()
nodes_pipeline.buildNodeTrees()
nodes_world.buildNodeTrees(shader_references, asset_references) # TODO: Have to build nodes everytime to collect env map resources, should be cached
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# TODO: Set armatures to center of world so skin transform is zero
armatures = []
for o in bpy.data.objects:
if o.type == 'ARMATURE':
a = Object()
a.armature = o
a.x = o.location.x
a.y = o.location.y
a.z = o.location.z
armatures.append(a)
o.location.x = 0
o.location.y = 0
o.location.z = 0
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Export scene data
for scene in bpy.data.scenes:
if scene.name[0] != '.': # Skip hidden scenes
bpy.ops.export_scene.armory({"scene":scene}, filepath='Assets/generated/' + scene.name + '.json')
shader_references += ArmoryExporter.shader_references
asset_references += ArmoryExporter.asset_references
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Move armatures back
for a in armatures:
a.armature.location.x = a.x
a.armature.location.y = a.y
a.armature.location.z = a.z
# Write khafile.js
write_data.write_khafilejs(shader_references, asset_references)
2015-12-07 20:04:23 +01:00
2016-02-08 17:28:05 +01:00
# Write Main.hx
write_data.write_main()
2016-01-17 22:31:31 +01:00
2016-02-08 17:28:05 +01:00
def buildProject(self, build_type=0):
# Save blend
bpy.ops.wm.save_mainfile()
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Save scripts
#area = bpy.context.area
#old_type = area.type
#area.type = 'TEXT_EDITOR'
#for text in bpy.data.texts:
#area.spaces[0].text = text
#bpy.ops.text.save()
##bpy.ops.text.save()
#area.type = old_type
# Set dir
s = bpy.data.filepath.split(os.path.sep)
name = s.pop()
name = name.split(".")
name = name[0]
fp = os.path.sep.join(s)
os.chdir(fp)
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Export
exportGameData()
# Set build command
if (bpy.data.worlds[0]['CGProjectTarget'] == 0):
bashCommand = "-t html5"
elif (bpy.data.worlds[0]['CGProjectTarget'] == 1):
bashCommand = "-t windows"
elif (bpy.data.worlds[0]['CGProjectTarget'] == 2):
bashCommand = "-t osx"
elif (bpy.data.worlds[0]['CGProjectTarget'] == 3):
bashCommand = "-t linux"
elif (bpy.data.worlds[0]['CGProjectTarget'] == 4):
bashCommand = "-t ios"
elif (bpy.data.worlds[0]['CGProjectTarget'] == 5):
bashCommand = "-t android-native"
# Build
haxelib_path = "haxelib"
if platform.system() == 'Darwin':
haxelib_path = "/usr/local/bin/haxelib"
2015-10-31 00:30:36 +01:00
2016-02-08 17:28:05 +01:00
prefix = haxelib_path + " run kha "
2015-10-31 00:30:36 +01:00
2016-02-08 17:28:05 +01:00
output = subprocess.check_output([haxelib_path + " path cyclesgame"], shell=True)
output = str(output).split("\\n")[0].split("'")[1]
scripts_path = output[:-8] + "blender/"
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
blender_path = bpy.app.binary_path
blend_path = bpy.data.filepath
p = subprocess.Popen([blender_path, blend_path, '-b', '-P', scripts_path + 'lib/build.py', '--', bashCommand, str(build_type), str(bpy.data.worlds[0]['CGProjectTarget'])])
#p = subprocess.Popen([blender_path, blend_path, '-b', '-P', scripts_path + 'lib/build.py', '--', prefix + bashCommand, str(build_type), str(bpy.data.worlds[0]['CGProjectTarget'])])
atexit.register(p.terminate)
self.report({'INFO'}, "Building, see console...")
2015-10-30 13:23:09 +01:00
def cleanProject(self):
2016-02-08 17:28:05 +01:00
# Set dir
s = bpy.data.filepath.split(os.path.sep)
name = s.pop()
name = name.split(".")
name = name[0]
fp = os.path.sep.join(s)
os.chdir(fp)
# Remove build data
if os.path.isdir("build"):
shutil.rmtree('build')
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Remove generated data
if os.path.isdir("Assets/generated"):
shutil.rmtree('Assets/generated')
2016-01-17 22:31:31 +01:00
2016-02-08 17:28:05 +01:00
# Remove compiled nodes
nodes_path = "Sources/" + bpy.data.worlds[0].CGProjectPackage.replace(".", "/") + "/node/"
if os.path.isdir(nodes_path):
shutil.rmtree(nodes_path)
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
self.report({'INFO'}, "Done")
2015-10-30 13:23:09 +01:00
# Play
class OBJECT_OT_PLAYButton(bpy.types.Operator):
2016-02-08 17:28:05 +01:00
bl_idname = "cg.play"
bl_label = "Play"
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
def execute(self, context):
buildProject(self, 1)
return{'FINISHED'}
2015-10-30 13:23:09 +01:00
# Build
class OBJECT_OT_BUILDButton(bpy.types.Operator):
2016-02-08 17:28:05 +01:00
bl_idname = "cg.build"
bl_label = "Build"
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
def execute(self, context):
buildProject(self, 0)
return{'FINISHED'}
2015-10-30 13:23:09 +01:00
# Open project folder
class OBJECT_OT_FOLDERButton(bpy.types.Operator):
2016-02-08 17:28:05 +01:00
bl_idname = "cg.folder"
bl_label = "Folder"
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
def execute(self, context):
s = bpy.data.filepath.split(os.path.sep)
name = s.pop()
name = name.split(".")
name = name[0]
fp = os.path.sep.join(s)
webbrowser.open('file://' + fp)
return{'FINISHED'}
2015-10-30 13:23:09 +01:00
# Clean project
class OBJECT_OT_CLEANButton(bpy.types.Operator):
2016-02-08 17:28:05 +01:00
bl_idname = "cg.clean"
bl_label = "Clean"
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
def execute(self, context):
cleanProject(self)
return{'FINISHED'}
2015-10-30 13:23:09 +01:00
# Registration
2015-12-07 20:04:23 +01:00
def register():
2016-02-08 17:28:05 +01:00
bpy.utils.register_module(__name__)
# Store properties in world
initWorldProperties()
2015-12-07 20:04:23 +01:00
def unregister():
2016-02-08 17:28:05 +01:00
bpy.utils.unregister_module(__name__)