Packed assets support.

This commit is contained in:
Lubos Lenco 2016-08-22 21:56:28 +02:00
parent 9a49e84f43
commit 878a27ee93
70 changed files with 451 additions and 241 deletions

3
Sources/armory/App.hx Normal file
View file

@ -0,0 +1,3 @@
package armory;
typedef App = iron.App;

View file

@ -1,47 +1,3 @@
package armory;
import iron.App;
import iron.node.Node;
import iron.node.CameraNode;
import iron.resource.SceneFormat;
import iron.resource.Resource;
import armory.trait.internal.PhysicsWorld;
class Root {
var cam:CameraNode;
public static var physics:PhysicsWorld;
public function new() {
// Startup scene
var sceneNode = iron.Root.addScene(Main.projectScene);
cam = iron.Root.cameras[0];
// Attach world to camera for now
var resource:TSceneFormat = Resource.getSceneResource(Main.projectScene);
cam.world = Resource.getWorld(Main.projectScene, resource.world_ref);
// Physics
physics = new PhysicsWorld(resource.gravity);
sceneNode.addTrait(physics);
App.notifyOnRender(render);
// Experimental scene reloading
// App.notifyOnUpdate(function() {
// if (iron.sys.Input.released) {
// // kha.Assets.loadBlob(Main.projectScene + '_arm', function(b:kha.Blob) {
// iron.App.reset();
// iron.resource.Resource.clearSceneData();
// new iron.App(armory.Root);
// // });
// }
// });
}
function render(g:kha.graphics4.Graphics) {
cam.renderFrame(g, iron.Root.root, iron.Root.lights);
}
}
typedef Root = iron.Root;

48
Sources/armory/Scene.hx Normal file
View file

@ -0,0 +1,48 @@
package armory;
import iron.App;
import iron.Root;
import iron.node.Node;
import iron.node.CameraNode;
import iron.resource.SceneFormat;
import iron.resource.Resource;
import armory.trait.internal.PhysicsWorld;
class Scene {
var cam:CameraNode;
public static var physics:PhysicsWorld;
public function new(sceneId:String) {
// Startup scene
var sceneNode = Root.addScene(sceneId);
cam = Root.cameras[0];
// Attach world to camera for now
var resource:TSceneFormat = Resource.getSceneResource(sceneId);
cam.world = Resource.getWorld(sceneId, resource.world_ref);
// Physics
physics = new PhysicsWorld(resource.gravity);
sceneNode.addTrait(physics);
App.notifyOnRender(render);
// Experimental scene reloading
// App.notifyOnUpdate(function() {
// if (iron.sys.Input.released) {
// // kha.Assets.loadBlob(sceneId + '_arm', function(b:kha.Blob) {
// iron.App.reset();
// iron.resource.Resource.clearSceneData();
// new iron.App(armory.Root);
// // });
// }
// });
}
function render(g:kha.graphics4.Graphics) {
cam.renderFrame(g, Root.root, Root.lights);
}
}

3
Sources/armory/Trait.hx Normal file
View file

@ -0,0 +1,3 @@
package armory;
typedef Trait = iron.Trait;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class BoolNode extends Node {

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class FloatNode extends Node {

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import armory.trait.internal.NodeExecutor;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class IntNode extends Node {

View file

@ -0,0 +1,28 @@
package armory.logicnode;
import armory.trait.internal.NodeExecutor;
class Node {
var executor:NodeExecutor;
var parents:Array<Node> = [];
public var inputs:Array<Dynamic> = [];
public function new() {}
public function start(executor:NodeExecutor, parent:Node = null) {
this.executor = executor;
if (parent != null) parents.push(parent);
for (inp in inputs) inp.start(executor, this);
inputChanged();
}
public function inputChanged() {
for (p in parents) {
p.inputChanged();
}
}
}

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import armory.trait.internal.NodeExecutor;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class ScaleValueNode extends FloatNode {

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import armory.trait.internal.NodeExecutor;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import armory.trait.internal.NodeExecutor;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class SineNode extends FloatNode {

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class StringNode extends Node {

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import armory.trait.internal.NodeExecutor;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import armory.trait.internal.NodeExecutor;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
import iron.math.Mat4;
import iron.math.Vec4;

View file

@ -1,4 +1,4 @@
package armory.node;
package armory.logicnode;
class VectorNode extends Node {

View file

@ -0,0 +1,3 @@
package armory.math;
typedef Mat4 = iron.math.Mat4;

View file

@ -0,0 +1,3 @@
package armory.math;
typedef Quat = iron.math.Quat;

View file

@ -0,0 +1,3 @@
package armory.math;
typedef Ray = iron.math.Ray;

View file

@ -0,0 +1,3 @@
package armory.math;
typedef RayCaster = iron.math.RayCaster;

View file

@ -0,0 +1,3 @@
package armory.math;
typedef Vec4 = iron.math.Vec4;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef Animation = iron.node.Animation;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef CameraNode = iron.node.CameraNode;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef DecalNode = iron.node.DecalNode;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef LightNode = iron.node.LightNode;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef ModelNode = iron.node.ModelNode;

View file

@ -1,28 +1,3 @@
package armory.node;
import armory.trait.internal.NodeExecutor;
class Node {
var executor:NodeExecutor;
var parents:Array<Node> = [];
public var inputs:Array<Dynamic> = [];
public function new() {}
public function start(executor:NodeExecutor, parent:Node = null) {
this.executor = executor;
if (parent != null) parents.push(parent);
for (inp in inputs) inp.start(executor, this);
inputChanged();
}
public function inputChanged() {
for (p in parents) {
p.inputChanged();
}
}
}
typedef Node = iron.node.Node;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef ParticleSystem = iron.node.ParticleSystem;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef SpeakerNode = iron.node.SpeakerNode;

View file

@ -0,0 +1,3 @@
package armory.node;
typedef Transform = iron.node.Transform;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef CameraResource = iron.resource.CameraResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef Geometry = iron.resource.Geometry;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef LightResource = iron.resource.LightResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef MaterialResource = iron.resource.MaterialResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef ModelResource = iron.resource.ModelResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef ParticleResource = iron.resource.ParticleResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef PipelineResource = iron.resource.PipelineResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef RenderPath = iron.resource.RenderPath;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef Resource = iron.resource.Resource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef SceneFormat = iron.resource.SceneFormat;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef ShaderResource = iron.resource.ShaderResource;

View file

@ -0,0 +1,3 @@
package armory.resource;
typedef WorldResource = iron.resource.WorldResource;

View file

@ -0,0 +1,3 @@
package armory.sys;
typedef Audio = iron.sys.Audio;

View file

@ -0,0 +1,3 @@
package armory.sys;
typedef Input = iron.sys.Input;

View file

@ -0,0 +1,3 @@
package armory.sys;
typedef Storage = iron.sys.Storage;

View file

@ -0,0 +1,3 @@
package armory.sys;
typedef Time = iron.sys.Time;

View file

@ -0,0 +1,3 @@
package armory.sys;
typedef Tween = iron.sys.Tween;

3
Sources/armory/sys/VR.hx Normal file
View file

@ -0,0 +1,3 @@
package armory.sys;
typedef VR = iron.sys.VR;

View file

@ -31,7 +31,7 @@ class PhysicsDrag extends Trait {
}
function init() {
physics = Root.physics;
physics = armory.Scene.physics;
}
function update() {

View file

@ -67,7 +67,7 @@ class VehicleBody extends Trait {
}
function init() {
physics = armory.Root.physics;
physics = armory.Scene.physics;
transform = node.transform;
camera = iron.Root.cameras[0];

View file

@ -7,7 +7,7 @@ class JSScript extends Trait {
static var api:JSScriptAPI = null;
#if cpp
static var ctx:haxeduktape.DukContext = null;
// static var ctx:haxeduktape.DukContext = null;
#end
public function new(scriptBlob:String) {
@ -22,12 +22,11 @@ class JSScript extends Trait {
untyped __js__("eval(src);");
#else
if (ctx == null) {
ctx = new haxeduktape.DukContext();
api = new JSScriptAPI(ctx);
}
ctx.evalString(src);
// ctx.evalString('print(123)');
// if (ctx == null) {
// ctx = new haxeduktape.DukContext();
// api = new JSScriptAPI(ctx);
// }
// ctx.evalString(src);
#end
}
}

View file

@ -2,43 +2,48 @@ package armory.trait.internal;
#if cpp
import haxeduktape.Duktape;
// import haxeduktape.Duktape;
// @:headerCode('
// #include <duktape.h>
// ')
class JSScriptAPI {
/*
static var ctx:haxeduktape.DukContext;
var ctx:haxeduktape.DukContext;
static function console_log(rawctx:cpp.RawPointer<Duk_context>):Int {
var arg = ctx.requireNumber(0);
trace(arg);
// ctx.pushNumber(arg * arg);
return 1;
}
// static function do_tweak(ctx:cpp.RawPointer<Duk_context>):Int {
// return 1;
// }
public function new(_ctx:haxeduktape.DukContext) {
ctx = _ctx;
var rawctx = ctx.ctx;
public function new(ctx:haxeduktape.DukContext) {
this.ctx = ctx;
// Console
ctx.pushGlobalObject();
ctx.pushObject();
// ctx.pushGlobalObject();
// ctx.pushObject();
untyped __cpp__('
const duk_function_list_entry console_module_funcs[] = {
{ "log", console_log, 1 },
{ NULL, NULL, 0 }
}');
//untyped __cpp__('
// const duk_function_list_entry my_module_funcs[] = {
// { "tweak", do_tweak, 0 },
// { NULL, NULL, 0 }
// }');
untyped __cpp__("duk_put_function_list(rawctx, -1, console_module_funcs)");
// var rawctx = ctx.ctx;
// untyped __cpp__("duk_put_function_list(rawctx, -1, my_module_funcs)");
// ctx.putPropString("MyModule");
// ctx.pop();
ctx.putPropString("console");
ctx.pop();
}
*/
}
#else
@:expose("arm")
@:expose("armory")
class JSScriptAPI {
public static var App = iron.App;
@ -50,7 +55,7 @@ class JSScriptAPI {
public function new() { }
}
@:expose("arm.math")
@:expose("armory.math")
class JSScriptAPIMath {
public static var Vec4 = iron.math.Vec4;

View file

@ -4,7 +4,7 @@ import iron.Trait;
class NodeExecutor extends Trait {
var baseNode:armory.node.Node;
var baseNode:armory.logicnode.Node;
var nodeInits:Array<Void->Void> = [];
var nodeUpdates:Array<Void->Void> = [];
@ -14,7 +14,7 @@ class NodeExecutor extends Trait {
notifyOnUpdate(update);
}
public function start(baseNode:armory.node.Node) {
public function start(baseNode:armory.logicnode.Node) {
this.baseNode = baseNode;
baseNode.start(this);
}

View file

@ -8,7 +8,6 @@ import iron.sys.Time;
import iron.math.Vec4;
import iron.node.Transform;
import iron.node.ModelNode;
import armory.Root;
class RigidBody extends Trait {
@ -53,7 +52,7 @@ class RigidBody extends Trait {
public function init() {
transform = node.transform;
physics = Root.physics;
physics = armory.Scene.physics;
if (bodyCreated) return;
bodyCreated = true;

View file

@ -2,15 +2,17 @@ assets = []
khafile_defs = []
def reset():
global assets
global khafile_defs
assets = []
khafile_defs = []
global assets
global khafile_defs
assets = []
khafile_defs = []
def add(file):
global assets
assets.append(file)
global assets
if file not in assets:
assets.append(file)
def add_khafile_def(d):
global khafile_defs
khafile_defs.append(d)
if d not in khafile_defs:
khafile_defs.append(d)

View file

@ -2160,7 +2160,22 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['id'] = objectRef[1]["structName"]
object = objectRef[0]
if object.sound:
o['sound'] = object.sound.name.split('.')[0]
# Packed
if object.sound.packed_file != None:
unpack_path = utils.get_fp() + '/build/compiled/Assets/unpacked'
if not os.path.exists(unpack_path):
os.makedirs(unpack_path)
unpack_filepath = unpack_path + '/' + object.sound.name
if os.path.isfile(unpack_filepath) == False or os.path.getsize(unpack_filepath) != object.sound.packed_file.size:
with open(unpack_filepath, 'wb') as f:
f.write(object.sound.packed_file.data)
assets.add(unpack_filepath)
# External
else:
assets.add(utils.safe_assetpath(object.sound.filepath)) # Link sound to assets
o['sound'] = utils.extract_filename_noext(object.sound.filepath)
o['sound'] = utils.safe_filename(o['sound'])
else:
o['sound'] = ''
self.output['speaker_resources'].append(o)
@ -2493,7 +2508,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.JSScript'
x['parameters'] = [utils.safe_filename(t.jsscript_prop)]
scriptspath = utils.get_fp() + '/' + 'compiled/scripts/'
scriptspath = utils.get_fp() + '/build/compiled/scripts/'
if not os.path.exists(scriptspath):
os.makedirs(scriptspath)
# Compile to JS
@ -2506,17 +2521,24 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path
python_path = '/Applications/Blender/blender.app/Contents/Resources/2.77/python/bin/python3.5m'
# Extract path to built-in python binary
if utils.get_os() == 'win':
# Remove 'os.py' from path
python_path = os.__file__[:-5] + '../bin/python.exe'
elif utils.get_os() == 'mac':
python_path = os.__file__[:-5] + '../../bin/python3.5m'
else:
python_path = os.__file__[:-5] + '../../bin/python3.5m'
cwd = os.getcwd()
os.chdir(scriptspath)
# Disable minification for now, too slow
subprocess.Popen([python_path + ' ' + sdk_path + '/lib/transcrypt/__main__.py' + ' ' + pyname + ' --nomin'], shell=True)
os.chdir(cwd)
# Compiled file
assets.add('compiled/scripts/__javascript__/' + t.jsscript_prop + '.js')
assets.add('build/compiled/scripts/__javascript__/' + t.jsscript_prop + '.js')
else:
# Write js to file
assetpath = 'compiled/scripts/' + t.jsscript_prop + '.js'
assetpath = 'build/compiled/scripts/' + t.jsscript_prop + '.js'
targetpath = utils.get_fp() + '/' + assetpath
with open(targetpath, 'w') as f:
f.write(bpy.data.texts[t.jsscript_prop].as_string())
@ -2799,12 +2821,12 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
# Shader res
shader_res_name = ArmoryExporter.pipeline_id + ext
shader_res_path = 'compiled/ShaderResources/' + ArmoryExporter.pipeline_id + '/' + shader_res_name + '.arm'
shader_res_path = 'build/compiled/ShaderResources/' + ArmoryExporter.pipeline_id + '/' + shader_res_name + '.arm'
# Stencil mask
# if material.stencil_mask > 0:
# mask_ext = "_mask" + str(material.stencil_mask)
# shader_res_name_with_mask = shader_res_name + mask_ext
# shader_res_path_with_mask = 'compiled/ShaderResources/' + ArmoryExporter.pipeline_id + '/' + shader_res_name_with_mask + '.arm'
# shader_res_path_with_mask = 'build/compiled/ShaderResources/' + ArmoryExporter.pipeline_id + '/' + shader_res_name_with_mask + '.arm'
# # Copy resource if it does not exist and set stencil mask
# if not os.path.isfile(shader_res_path_with_mask):
# json_file = open(shader_res_path).read()
@ -2825,7 +2847,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
# Process all passes from pipeline
for pipe_pass in pipeline_passes:
shader_name = pipe_pass + ext
ArmoryExporter.shader_references.append('compiled/Shaders/' + ArmoryExporter.pipeline_id + '/' + shader_name)
ArmoryExporter.shader_references.append('build/compiled/Shaders/' + ArmoryExporter.pipeline_id + '/' + shader_name)
def register():
bpy.utils.register_class(ArmoryExporter)

View file

@ -220,7 +220,7 @@ def make(json_name, fp, minimize, defs):
base_name = json_name.split('.', 1)[0]
# Make out dir
path = fp + '/compiled/ShaderResources/' + base_name
path = fp + '/build/compiled/ShaderResources/' + base_name
if not os.path.exists(path):
os.makedirs(path)

View file

@ -31,7 +31,7 @@ def make(json_name, fp, defs=None):
base_name = json_name.split('.', 1)[0]
# Make out dir
path = fp + '/compiled/Shaders/' + base_name
path = fp + '/build/compiled/Shaders/' + base_name
if not os.path.exists(path):
os.makedirs(path)

View file

@ -211,7 +211,7 @@ def buildNodeTree(node_group):
with open(path + node_group_name + '.hx', 'w') as f:
f.write('package ' + bpy.data.worlds[0].ArmProjectPackage + '.node;\n\n')
f.write('import armory.node.*;\n\n')
f.write('import armory.logicnode.*;\n\n')
f.write('class ' + node_group_name + ' extends armory.trait.internal.NodeExecutor {\n\n')
f.write('\tpublic function new() { super(); notifyOnAdd(add); }\n\n')
f.write('\tfunction add() {\n')

View file

@ -1,5 +1,8 @@
import bpy
import math
import assets
import utils
import os
def is_pow(num):
return ((num & (num - 1)) == 0) and num != 0
@ -89,11 +92,25 @@ def make_texture(self, id, image_node, material):
tex = {}
tex['id'] = id
image = image_node.image
if image is not None:
tex['name'] = image.filepath.rsplit('/', 1)[1].rsplit('.', 1)[0] # Extract file name without extension
tex['name'] = tex['name'].replace('.', '_')
tex['name'] = tex['name'].replace('-', '_')
tex['name'] = tex['name'].replace(' ', '_')
if image != None:
if image.packed_file != None:
# Extract packed data
unpack_path = utils.get_fp() + '/build/compiled/Assets/unpacked'
if not os.path.exists(unpack_path):
os.makedirs(unpack_path)
unpack_filepath = unpack_path + '/' + image.name
# Write bytes if size is different or file does not exist yet
if os.path.isfile(unpack_filepath) == False or os.path.getsize(unpack_filepath) != image.packed_file.size:
with open(unpack_filepath, 'wb') as f:
f.write(image.packed_file.data)
# Add asset
assets.add(unpack_filepath)
else:
# Link image path to assets
assets.add(utils.safe_assetpath(image.filepath))
# Reference image name
tex['name'] = utils.extract_filename_noext(image.filepath)
tex['name'] = utils.safe_filename(tex['name'])
if image_node.interpolation == 'Cubic': # Mipmap linear
tex['mipmap_filter'] = 'linear'
tex['generate_mipmaps'] = True

View file

@ -788,8 +788,8 @@ def buildNodeTrees(shader_references, asset_references, assets_path):
os.chdir(fp)
# Make sure Assets dir exists
if not os.path.exists('compiled/Assets/pipelines'):
os.makedirs('compiled/Assets/pipelines')
if not os.path.exists('build/compiled/Assets/pipelines'):
os.makedirs('build/compiled/Assets/pipelines')
buildNodeTrees.assets_path = assets_path
buildNodeTrees.linked_assets = []
@ -807,7 +807,7 @@ def buildNodeTree(node_group, shader_references, asset_references):
res = {}
output['pipeline_resources'] = [res]
path = 'compiled/Assets/pipelines/'
path = 'build/compiled/Assets/pipelines/'
node_group_name = node_group.name.replace('.', '_')
rn = get_root_node(node_group)
@ -937,8 +937,8 @@ def make_draw_material_quad(stage, node_group, node, shader_references, asset_re
dir_name = scon[2]
# No world defs for material passes
res_name = scon[2]
asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.arm')
shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name)
asset_references.append('build/compiled/ShaderResources/' + dir_name + '/' + res_name + '.arm')
shader_references.append('build/compiled/Shaders/' + dir_name + '/' + res_name)
def make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=1, shader_context=None):
stage['command'] = 'draw_shader_quad'
@ -952,8 +952,8 @@ def make_draw_quad(stage, node_group, node, shader_references, asset_references,
dir_name = scon[0]
# Append world defs
res_name = scon[1] + world_defs
asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.arm')
shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name)
asset_references.append('build/compiled/ShaderResources/' + dir_name + '/' + res_name + '.arm')
shader_references.append('build/compiled/Shaders/' + dir_name + '/' + res_name)
def make_draw_world(stage, node_group, node, shader_references, asset_references, dome=True):
if dome:
@ -1000,8 +1000,8 @@ def make_draw_compositor(stage, node_group, node, shader_references, asset_refer
stage['command'] = 'draw_shader_quad'
stage['params'].append(res_name + '/' + res_name + '/' + scon)
# Include resource and shaders
asset_references.append('compiled/ShaderResources/' + scon + '/' + res_name + '.arm')
shader_references.append('compiled/Shaders/' + scon + '/' + res_name)
asset_references.append('build/compiled/ShaderResources/' + scon + '/' + res_name + '.arm')
shader_references.append('build/compiled/Shaders/' + scon + '/' + res_name)
# Link assets
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise256.png')

View file

@ -32,8 +32,8 @@ def buildNodeTrees():
os.chdir(fp)
# Make sure Assets dir exists
if not os.path.exists('compiled/Assets/materials'):
os.makedirs('compiled/Assets/materials')
if not os.path.exists('build/compiled/Assets/materials'):
os.makedirs('build/compiled/Assets/materials')
# Export world nodes
world_outputs = []
@ -93,11 +93,11 @@ def write_output(output, asset_references, shader_references):
# Reference correct shader context
res = output['material_resources'][0]
res['shader'] = res_name + '/' + res_name
asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.arm')
shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name)
asset_references.append('build/compiled/ShaderResources/' + dir_name + '/' + res_name + '.arm')
shader_references.append('build/compiled/Shaders/' + dir_name + '/' + res_name)
# Write material json
path = 'compiled/Assets/materials/'
path = 'build/compiled/Assets/materials/'
asset_path = path + res['id'] + '.arm'
utils.write_arm(asset_path, output)
assets.add(asset_path)

View file

@ -18,6 +18,9 @@ import lib.make_resources
import lib.make_variants
import utils
import assets
# Server
import http.server
import socketserver
def init_armory_props():
# First run
@ -175,7 +178,7 @@ def export_game_data(fp, sdk_path):
# Export scene data
for scene in bpy.data.scenes:
if scene.game_export:
asset_path = 'compiled/Assets/' + scene.name + '.arm'
asset_path = 'build/compiled/Assets/' + scene.name + '.arm'
bpy.ops.export_scene.armory(
get_export_scene_override(scene),
filepath=asset_path)
@ -193,13 +196,13 @@ def export_game_data(fp, sdk_path):
if bpy.data.worlds[0].ArmCacheShaders == False:
if os.path.isdir('build/html5-resources'):
shutil.rmtree('build/html5-resources')
if os.path.isdir('compiled/Shaders'):
shutil.rmtree('compiled/Shaders')
if os.path.isdir('compiled/ShaderResources'):
shutil.rmtree('compiled/ShaderResources')
if os.path.isdir('build/compiled/Shaders'):
shutil.rmtree('build/compiled/Shaders')
if os.path.isdir('build/compiled/ShaderResources'):
shutil.rmtree('build/compiled/ShaderResources')
# Remove shader resources if shaders were deleted
elif os.path.isdir('compiled/Shaders') == False and os.path.isdir('compiled/ShaderResources') == True:
shutil.rmtree('compiled/ShaderResources')
elif os.path.isdir('build/compiled/Shaders') == False and os.path.isdir('build/compiled/ShaderResources') == True:
shutil.rmtree('build/compiled/ShaderResources')
# Write referenced shader variants
# Assume asset_references contains shader resources only for now
@ -207,7 +210,7 @@ def export_game_data(fp, sdk_path):
# Resource does not exist yet
os.chdir(fp)
if not os.path.exists(ref):
shader_name = ref.split('/')[2]
shader_name = ref.split('/')[3] # Extract from 'build/compiled/...'
strdefs = ref[:-4] # Remove '.arm' extension
defs = strdefs.split(shader_name) # 'name/name_def_def'
if len(defs) > 2:
@ -278,7 +281,7 @@ def compile_project(self, target_name=None):
def build_project(self):
# Save blend
bpy.ops.wm.save_mainfile()
# Get paths
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
@ -296,8 +299,6 @@ def build_project(self):
# Create directories
if not os.path.exists('Sources'):
os.makedirs('Sources')
if not os.path.exists('Assets'):
os.makedirs('Assets')
if not os.path.isdir('build/html5'):
os.makedirs('build/html5')
if not os.path.isdir('build/debug-html5'):
@ -307,6 +308,30 @@ def build_project(self):
if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].pipeline_path == 'pathtrace_path':
path_tracer.compile(raw_path + 'pt_trace_pass/pt_trace_pass.frag.glsl')
# Save external scripts edited inside Blender
write_texts = False
for text in bpy.data.texts:
if text.filepath != '' and text.is_dirty:
write_texts = True
break
if write_texts:
area = bpy.context.area
old_type = area.type
area.type = 'TEXT_EDITOR'
for text in bpy.data.texts:
if text.filepath != '' and text.is_dirty:
area.spaces[0].text = text
bpy.ops.text.save()
area.type = old_type
# Save internal Haxe scripts
for text in bpy.data.texts:
if text.filepath == '' and text.name[-3:] == '.hx':
with open('Sources/' + bpy.data.worlds[0].ArmProjectPackage + '/' + text.name, 'w') as f:
f.write(text.as_string())
# Save internal assets
# Export data
export_game_data(fp, sdk_path)
@ -339,82 +364,104 @@ def play_project(self, in_viewport):
# Build data
build_project(self)
if in_viewport == False:
# Windowed player
wrd = bpy.data.worlds[0]
x = 0
y = 0
w, h = utils.get_render_resolution()
winoff = 0
else:
# Player dimensions
if utils.get_os() == 'win':
psize = 1 # Scale in electron
xoff = 0
yoff = 6
elif utils.get_os() == 'mac':
psize = bpy.context.user_preferences.system.pixel_size
xoff = 5
yoff = 22
wrd = bpy.data.worlds[0]
if wrd.ArmPlayRuntime == 'Native':
compile_project(self, target_name='--run')
else: # Electron, Browser
if in_viewport == False:
# Windowed player
x = 0
y = 0
w, h = utils.get_render_resolution()
winoff = 0
else:
psize = 1
xoff = 0
yoff = 6
# Player dimensions
if utils.get_os() == 'win':
psize = 1 # Scale in electron
xoff = 0
yoff = 6
elif utils.get_os() == 'mac':
psize = bpy.context.user_preferences.system.pixel_size
xoff = 5
yoff = 22
else:
psize = 1
xoff = 0
yoff = 6
x = bpy.context.window.x + (bpy.context.area.x - xoff) / psize
y = bpy.context.window.height + 45 - (bpy.context.area.y + bpy.context.area.height) / psize
w = (bpy.context.area.width + xoff) / psize
h = (bpy.context.area.height) / psize - 25
winoff = bpy.context.window.y + bpy.context.window.height + yoff
x = bpy.context.window.x + (bpy.context.area.x - xoff) / psize
y = bpy.context.window.height + 45 - (bpy.context.area.y + bpy.context.area.height) / psize
w = (bpy.context.area.width + xoff) / psize
h = (bpy.context.area.height) / psize - 25
winoff = bpy.context.window.y + bpy.context.window.height + yoff
write_data.write_electronjs(x, y, w, h, winoff, in_viewport)
write_data.write_indexhtml(w, h, in_viewport)
write_data.write_electronjs(x, y, w, h, winoff, in_viewport)
write_data.write_indexhtml(w, h, in_viewport)
# Compile
play_project.compileproc = compile_project(self, target_name='html5')
watch_compile()
# Compile
play_project.compileproc = compile_project(self, target_name='html5')
watch_compile()
def run_server():
Handler = http.server.SimpleHTTPRequestHandler
try:
httpd = socketserver.TCPServer(("", 8040), Handler)
httpd.serve_forever()
except:
print('Server already running')
def on_compiled():
print_info("Ready")
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path
electron_app_path = './build/electron.js'
if utils.get_os() == 'win':
electron_path = sdk_path + 'kode_studio/KodeStudio-win32/Kode Studio.exe'
elif utils.get_os() == 'mac':
# electron_path = sdk_path + 'kode_studio/Kode Studio.app/Contents/MacOS/Electron'
electron_path = sdk_path + 'kode_studio/Electron.app/Contents/MacOS/Electron'
else:
electron_path = sdk_path + 'kode_studio/KodeStudio-linux64/kodestudio'
wrd = bpy.data.worlds[0]
if wrd.ArmPlayRuntime == 'Electron':
electron_app_path = './build/electron.js'
if utils.get_os() == 'win':
electron_path = sdk_path + 'kode_studio/KodeStudio-win32/Kode Studio.exe'
elif utils.get_os() == 'mac':
# electron_path = sdk_path + 'kode_studio/Kode Studio.app/Contents/MacOS/Electron'
electron_path = sdk_path + 'kode_studio/Electron.app/Contents/MacOS/Electron'
else:
electron_path = sdk_path + 'kode_studio/KodeStudio-linux64/kodestudio'
play_project.playproc = subprocess.Popen([electron_path, '--chromedebug', '--remote-debugging-port=9222', electron_app_path])
watch_play()
elif wrd.ArmPlayRuntime == 'Browser':
# Start server
os.chdir(utils.get_fp())
t = threading.Thread(name='localserver', target=run_server)
t.daemon = True
t.start()
html5_app_path = 'http://localhost:8040/build/html5'
webbrowser.open(html5_app_path)
play_project.playproc = subprocess.Popen([electron_path, '--chromedebug', '--remote-debugging-port=9222', electron_app_path])
watch_play()
play_project.playproc = None
play_project.compileproc = None
def clean_project(self):
os.chdir(utils.get_fp())
# Remove build data
# Remove build and compiled data
if os.path.isdir('build'):
shutil.rmtree('build')
# Remove generated assets and shader variants
if os.path.isdir('compiled'):
shutil.rmtree('compiled')
# Remove compiled nodes
nodes_path = 'Sources/' + bpy.data.worlds[0].ArmProjectPackage.replace('.', '/') + '/node/'
if os.path.isdir(nodes_path):
shutil.rmtree(nodes_path)
# Remove khafile/korefile
# Remove khafile/korefile/Main.hx
if os.path.isfile('khafile.js'):
os.remove('khafile.js')
if os.path.isfile('korefile.js'):
os.remove('korefile.js')
if os.path.isfile('Sources/Main.hx'):
os.remove('Sources/Main.hx')
self.report({'INFO'}, 'Done')

View file

@ -15,23 +15,27 @@ def on_scene_update(context):
elif edit_obj.type == 'ARMATURE':
edit_obj.data.armature_cached = False
for text in bpy.data.texts:
if text.is_updated:
print('ASDASDASDASDASD')
def invalidate_shader_cache(self, context):
# compiled.glsl changed, recompile all shaders next time
fp = utils.get_fp()
if os.path.isdir(fp + '/compiled/ShaderResources'):
shutil.rmtree(fp + '/compiled/ShaderResources')
if os.path.isdir(fp + '/build/compiled/ShaderResources'):
shutil.rmtree(fp + '/build/compiled/ShaderResources')
def invalidate_compiled_data(self, context):
fp = utils.get_fp()
if os.path.isdir(fp + '/compiled/Assets'):
shutil.rmtree(fp + '/compiled/Assets')
if os.path.isdir(fp + '/compiled/ShaderResources'):
shutil.rmtree(fp + '/compiled/ShaderResources')
if os.path.isdir(fp + '/build/compiled/Assets'):
shutil.rmtree(fp + '/build/compiled/Assets')
if os.path.isdir(fp + '/build/compiled/ShaderResources'):
shutil.rmtree(fp + '/build/compiled/ShaderResources')
def invalidate_geometry_data(self, context):
fp = utils.get_fp()
if os.path.isdir(fp + '/compiled/Assets/geoms'):
shutil.rmtree(fp + '/compiled/Assets/geoms')
if os.path.isdir(fp + '/build/compiled/Assets/geoms'):
shutil.rmtree(fp + '/build/compiled/Assets/geoms')
def initProperties():
# For project

View file

@ -49,7 +49,16 @@ def to_hex(val):
return '#%02x%02x%02x%02x' % (int(val[3] * 255), int(val[0] * 255), int(val[1] * 255), int(val[2] * 255))
def safe_filename(s):
return s.replace('.', '_').replace('-', '_').replace(' ', '_')
s = s.replace('.', '_').replace('-', '_').replace(' ', '_')
if s[0].isdigit(): # Prefix _ if first char is digit
s = '_' + s
return s
def safe_assetpath(s):
return s[2:] # Remove leading '//'
def extract_filename_noext(s):
return s.rsplit('/', 1)[1].rsplit('.', 1)[0] # Extract file name without extension
def get_render_resolution(scene_index=0):
render = bpy.data.scenes[scene_index].render

View file

@ -24,9 +24,8 @@ let project = new Project('""" + bpy.data.worlds[0].ArmProjectName + """');
project.addSources('Sources');
project.addShaders('Sources/Shaders/**');
project.addAssets('Assets/**');
""")
# project.addAssets('compiled/Assets/**');
# project.addAssets('build/compiled/Assets/**');
for file in assets.assets:
f.write("project.addAssets('" + file + "');\n")
@ -37,9 +36,8 @@ project.addAssets('Assets/**');
f.write("project.addDefine('WITH_PHYSICS');\n")
f.write(add_armory_library(sdk_path + '/lib/', 'haxebullet'))
###
f.write(add_armory_library(sdk_path + '/lib/', 'haxeduktape'))
###
# Native scripting
# f.write(add_armory_library(sdk_path + '/lib/', 'haxeduktape'))
for i in range(0, len(shader_references)): # Shaders
ref = shader_references[i]
@ -92,7 +90,7 @@ class Main {
static inline var projectWidth = """ + str(resx) + """;
static inline var projectHeight = """ + str(resy) + """;
static inline var projectSamplesPerPixel = """ + str(wrd.ArmProjectSamplesPerPixel) + """;
public static inline var projectScene = '""" + str(wrd.ArmProjectScene) + """';
static inline var projectScene = '""" + str(wrd.ArmProjectScene) + """';
public static function main() {
iron.sys.CompileTime.importPackage('armory.trait');
iron.sys.CompileTime.importPackage('armory.renderpipeline');
@ -121,7 +119,9 @@ class Main {
f.write("""
kha.System.init({title: projectName, width: projectWidth, height: projectHeight, samplesPerPixel: projectSamplesPerPixel}, function() {
new iron.App(armory.Root);
iron.App.init(function() {
new armory.Scene(projectScene);
});
});
}
}
@ -217,7 +217,7 @@ def write_indexhtml(w, h, in_viewport):
def write_compiledglsl(clip_start, clip_end, shadowmap_size):
wrd = bpy.data.worlds[0]
with open('compiled/Shaders/compiled.glsl', 'w') as f:
with open('build/compiled/Shaders/compiled.glsl', 'w') as f:
f.write(
"""const float PI = 3.1415926535;
const float PI2 = PI * 2.0;

View file

@ -17,19 +17,19 @@ def add_rad_assets(output_file_rad, rad_format, num_mips):
# Generate probes from environment map
def write_probes(image_filepath, disable_hdr, cached_num_mips, generate_radiance=True):
if not os.path.exists('compiled/Assets/envmaps'):
os.makedirs('compiled/Assets/envmaps')
if not os.path.exists('build/compiled/Assets/envmaps'):
os.makedirs('build/compiled/Assets/envmaps')
base_name = image_filepath.rsplit('/', 1)[1].rsplit('.', 1)[0] # Extract file name without extension
# Assets to be generated
output_file_irr = 'compiled/Assets/envmaps/' + base_name + '_irradiance'
output_file_irr = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
if generate_radiance:
output_file_rad = 'compiled/Assets/envmaps/' + base_name + '_radiance'
output_file_rad = 'build/compiled/Assets/envmaps/' + base_name + '_radiance'
rad_format = 'jpg' if disable_hdr else 'hdr'
# Assume irradiance has to exist
if os.path.exists('compiled/Assets/envmaps/' + base_name + '_irradiance.arm'):
if os.path.exists('build/compiled/Assets/envmaps/' + base_name + '_irradiance.arm'):
# Cached assets
add_irr_assets(output_file_irr)
if generate_radiance:
@ -218,10 +218,10 @@ def write_sky_irradiance(base_name):
# Predefined fake spherical harmonics for now
irradiance_floats = [1.0281457342829743,1.1617608778901902,1.3886220898440544,-0.13044863139637752,-0.2794659158733846,-0.5736106907295643,0.04065421813873111,0.0434367391348577,0.03567450494792305,0.10964557605577738,0.1129839085793664,0.11261660812141877,-0.08271974283263238,-0.08068091195339556,-0.06432614970480094,-0.12517787967665814,-0.11638582546310804,-0.09743696224655113,0.20068697715947176,0.2158788783296805,0.2109374396869599,0.19636637427150455,0.19445523113118082,0.17825330699680575,0.31440860839538637,0.33041120060402407,0.30867788630062676]
if not os.path.exists('compiled/Assets/envmaps'):
os.makedirs('compiled/Assets/envmaps')
if not os.path.exists('build/compiled/Assets/envmaps'):
os.makedirs('build/compiled/Assets/envmaps')
output_file = 'compiled/Assets/envmaps/' + base_name + '_irradiance'
output_file = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
sh_json = {}
sh_json['irradiance'] = irradiance_floats
@ -235,10 +235,10 @@ def write_color_irradiance(base_name, col):
for i in range(0, 24):
irradiance_floats.append(0.0)
if not os.path.exists('compiled/Assets/envmaps'):
os.makedirs('compiled/Assets/envmaps')
if not os.path.exists('build/compiled/Assets/envmaps'):
os.makedirs('build/compiled/Assets/envmaps')
output_file = 'compiled/Assets/envmaps/' + base_name + '_irradiance'
output_file = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
sh_json = {}
sh_json['irradiance'] = irradiance_floats