Prop descriptions.

This commit is contained in:
Lubos Lenco 2016-09-23 00:34:42 +02:00
parent e8ce9f2030
commit 80b8bc3236
46 changed files with 1496 additions and 1282 deletions

View file

@ -27,7 +27,7 @@ class TimeNode extends FloatNode {
function update() {
if (inputs[_enabled].b) {
f += iron.sys.Time.delta * scale;
f += iron.system.Time.delta * scale;
// Time out
if (inputs[_stopTime].f > 0) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
package armory.trait;
import iron.Trait;
import iron.sys.Input;
import iron.system.Input;
import iron.object.CameraObject;
import iron.math.Vec4;
import iron.math.Quat;

View file

@ -3,8 +3,8 @@ package armory.trait;
import iron.math.Mat4;
import iron.math.Vec4;
import iron.Trait;
import iron.sys.Input;
import iron.sys.Time;
import iron.system.Input;
import iron.system.Time;
import iron.object.Transform;
import iron.object.CameraObject;
import armory.trait.internal.RigidBody;

View file

@ -3,8 +3,8 @@ package armory.trait;
import iron.math.Mat4;
import iron.math.Vec4;
import iron.Trait;
import iron.sys.Input;
import iron.sys.Time;
import iron.system.Input;
import iron.system.Time;
import iron.object.Transform;
import iron.object.CameraObject;
import armory.trait.internal.RigidBody;

View file

@ -1,7 +1,7 @@
package armory.trait;
import iron.Trait;
import iron.sys.Input;
import iron.system.Input;
import armory.trait.internal.RigidBody;
import armory.trait.internal.PhysicsWorld;
#if WITH_PHYSICS
@ -42,7 +42,7 @@ class PhysicsDrag extends Trait {
if (Input.started) {
var b = physics.pickClosest(Input.x, Input.y);
if (b != null && b.mass > 0 && !b.body.ptr.isKinematicObject()) {
if (b != null && b.mass > 0 && !b.body.ptr.isKinematicObject() && b.object.getTrait(PhysicsDrag) != null) {
setRays();
pickedBody = b;
@ -53,6 +53,13 @@ class PhysicsDrag extends Trait {
var pickPos:BtVector3 = physics.rayCallback.value.m_hitPointWorld;
#end
// var ct = b.object.transform.matrix;
// var inv = iron.math.Mat4.identity();
// inv.getInverse(ct);
// var localPivotVec = new iron.math.Vec4(pickPos.x(), pickPos.y(), pickPos.z());
// localPivotVec.applyMat4(inv);
// var localPivot:BtVector3 = BtVector3.create(localPivotVec.x, localPivotVec.y, localPivotVec.z);
var ct = b.body.ptr.getCenterOfMassTransform();
var inv = ct.inverse();
@ -94,6 +101,8 @@ class PhysicsDrag extends Trait {
pickPos.z() - rayFrom.value.z());
pickDist = v.value.length();
Input.occupied = true;
}
}
@ -104,6 +113,8 @@ class PhysicsDrag extends Trait {
pickConstraint = null;
pickedBody = null;
}
Input.occupied = false;
}
else if (Input.touch) {

View file

@ -4,7 +4,7 @@ import iron.Trait;
import iron.object.Object;
import iron.object.CameraObject;
import iron.object.Transform;
import iron.sys.Time;
import iron.system.Time;
import armory.trait.internal.PhysicsWorld;
#if WITH_PHYSICS
import haxebullet.Bullet;

View file

@ -2,8 +2,8 @@ package armory.trait;
import kha.Key;
import iron.Trait;
import iron.sys.Input;
import iron.sys.Time;
import iron.system.Input;
import iron.system.Time;
import iron.object.CameraObject;
import iron.math.Vec4;
import iron.math.Quat;

View file

@ -34,7 +34,7 @@ class Animation extends Trait {
}
function update() {
object.setAnimationParams(iron.sys.Time.delta);
object.setAnimationParams(iron.system.Time.delta);
}
public function play(trackName:String, onTrackComplete:Void->Void = null) {

View file

@ -3,10 +3,10 @@ package armory.trait.internal;
import iron.Trait;
import iron.object.Object;
import iron.object.Transform;
import iron.sys.Input;
import iron.system.Input;
import iron.math.RayCaster;
class SceneEditor extends Trait {
class EditorSpace extends Trait {
var gizmo:Object;
var arrowX:Object;
@ -18,6 +18,8 @@ class SceneEditor extends Trait {
var moveY = false;
var moveZ = false;
static var first = true;
public function new() {
super();
@ -25,12 +27,25 @@ class SceneEditor extends Trait {
}
function init() {
gizmo = iron.Scene.active.getObject('ArrowGizmo');
arrowX = iron.Scene.active.getObject('ArrowX');
arrowY = iron.Scene.active.getObject('ArrowY');
arrowZ = iron.Scene.active.getObject('ArrowZ');
// gizmo = iron.Scene.active.getObject('ArrowGizmo');
// arrowX = iron.Scene.active.getObject('ArrowX');
// arrowY = iron.Scene.active.getObject('ArrowY');
// arrowZ = iron.Scene.active.getObject('ArrowZ');
notifyOnUpdate(update);
if (first) {
first = false;
kha.input.Keyboard.get().notify(onKeyDown, onKeyUp);
}
}
function onKeyDown(key: kha.Key, char: String) {
// if (char == 'Z') trace('__arm|quit');
if (key == kha.Key.ESC) trace('__arm|quit');
}
function onKeyUp(key: kha.Key, char: String) {
}
function update() {
@ -41,8 +56,8 @@ class SceneEditor extends Trait {
var hit = RayCaster.getClosestBoxIntersect(transforms, Input.x, Input.y, iron.Scene.active.camera);
if (hit != null) {
var loc = hit.loc;
gizmo.transform.loc.set(loc.x, loc.y, loc.z);
gizmo.transform.buildMatrix();
// gizmo.transform.loc.set(loc.x, loc.y, loc.z);
// gizmo.transform.buildMatrix();
selected = hit;
trace('__arm|select|' + selected.object.name);
}
@ -71,8 +86,8 @@ class SceneEditor extends Trait {
selected.buildMatrix();
gizmo.transform.loc.set(selected.loc.x, selected.loc.y, selected.loc.z);
gizmo.transform.buildMatrix();
// gizmo.transform.loc.set(selected.loc.x, selected.loc.y, selected.loc.z);
// gizmo.transform.buildMatrix();
}
}

View file

@ -48,7 +48,7 @@ class JSScriptAPI {
public static var App = iron.App;
public static var Scene = iron.Scene;
public static var Time = iron.sys.Time;
public static var Time = iron.system.Time;
public static var Object = iron.object.Object;
public static var Data = iron.data.Data;

View file

@ -4,7 +4,7 @@ package armory.trait.internal;
import haxebullet.Bullet;
#end
import iron.Trait;
import iron.sys.Time;
import iron.system.Time;
import iron.math.Vec4;
import iron.math.RayCaster;
@ -65,7 +65,9 @@ class PhysicsWorld extends Trait {
var gravity = iron.Scene.active.raw.gravity == null ? [0, 0, -9.81] : iron.Scene.active.raw.gravity;
world.ptr.setGravity(BtVector3.create(gravity[0], gravity[1], gravity[2]).value);
notifyOnUpdate(update);
Scene.active.notifyOnInit(function() {
notifyOnUpdate(update);
});
}
public function addRigidBody(body:RigidBody) {

View file

@ -4,7 +4,7 @@ package armory.trait.internal;
import haxebullet.Bullet;
#end
import iron.Trait;
import iron.sys.Time;
import iron.system.Time;
import iron.math.Vec4;
import iron.object.Transform;
import iron.object.MeshObject;
@ -41,9 +41,11 @@ class RigidBody extends Trait {
this.friction = friction;
this.collisionMargin = collisionMargin;
notifyOnInit(init);
notifyOnLateUpdate(lateUpdate);
notifyOnRemove(removeFromWorld);
Scene.active.notifyOnInit(function() {
notifyOnInit(init);
notifyOnLateUpdate(lateUpdate);
notifyOnRemove(removeFromWorld);
});
}
inline function withMargin(f:Float) {

Binary file not shown.

View file

@ -21,6 +21,7 @@ from bpy_extras.io_utils import ExportHelper
import assets
import utils
import subprocess
import make
kNodeTypeNode = 0
kNodeTypeBone = 1
@ -1450,7 +1451,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['material_refs'] = []
for i in range(len(bobject.material_slots)):
if self.object_has_override_material(bobject): # Overwrite material slot
if bobject.override_material: # Overwrite material slot
o['material_refs'].append(bobject.override_material_name)
else: # Export assigned material
self.ExportMaterialRef(bobject.material_slots[i].material, i, o)
@ -1967,7 +1968,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
om['instance_offsets'] = instance_offsets
# Export usage
om['static_usage'] = self.get_mesh_static_usage(bobject.data)
om['dynamic_usage'] = bobject.data.dynamic_usage
o['mesh'] = om
self.write_mesh(bobject, fp, o)
@ -2149,7 +2150,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['lamp_size'] = objref.shadow_soft_size
# Parse nodes, only emission for now
# Merge with nodes_material
# Merge with make_material
for n in objref.node_tree.nodes:
if n.type == 'EMISSION':
col = n.inputs[0].default_value
@ -2230,6 +2231,12 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['sound'] = utils.safe_filename(o['sound'])
else:
o['sound'] = ''
o['muted'] = objref.muted
o['loop'] = objref.loop
o['stream'] = objref.stream
o['volume'] = objref.volume
o['pitch'] = objref.pitch
o['attenuation'] = objref.attenuation
self.output['speaker_datas'].append(o)
def ExportMaterials(self):
@ -2467,12 +2474,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
bobject.data.mesh_cached_verts = len(bobject.data.vertices)
bobject.data.mesh_cached_edges = len(bobject.data.edges)
def object_has_override_material(self, bobject):
return bobject.override_material
def get_mesh_static_usage(self, data):
return data.static_usage
def get_export_tangents(self, mesh):
for m in mesh.materials:
if m.export_tangents == True:
@ -2592,7 +2593,10 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
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)
transproc = subprocess.Popen([python_path + ' ' + sdk_path + '/lib/transcrypt/__main__.py' + ' ' + pyname + ' --nomin'], shell=True)
transproc.wait()
if transproc.poll() != 0:
make.armory_log('Compiling ' + pyname + ' failed, check console')
os.chdir(cwd)
# Compiled file
assets.add('build/compiled/scripts/__javascript__/' + t.jsscript_prop + '.js')
@ -2771,10 +2775,10 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
decal_context = bpy.data.cameras[0].last_decal_context
# Parse nodes
import nodes_material
import make_material
# Parse from material output
if decal_uv_layer == None:
nodes_material.parse(self, material, c, defs)
make_material.parse(self, material, c, defs)
o['contexts'].append(c)
# Decal attached, split material into two separate ones
# Mandatory starting point from mix node for now
@ -2788,12 +2792,12 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
c2['bind_textures'] = []
defs2 = []
tree = material.node_tree
output_node = nodes_material.get_output_node(tree)
mix_node = nodes_material.find_node_by_link(tree, output_node, output_node.inputs[0])
surface_node1 = nodes_material.find_node_by_link(tree, mix_node, mix_node.inputs[1])
surface_node2 = nodes_material.find_node_by_link(tree, mix_node, mix_node.inputs[2])
nodes_material.parse_from(self, material, c, defs, surface_node1)
nodes_material.parse_from(self, material, c2, defs2, surface_node2)
output_node = make_material.get_output_node(tree)
mix_node = make_material.find_node_by_link(tree, output_node, output_node.inputs[0])
surface_node1 = make_material.find_node_by_link(tree, mix_node, mix_node.inputs[1])
surface_node2 = make_material.find_node_by_link(tree, mix_node, mix_node.inputs[2])
make_material.parse_from(self, material, c, defs, surface_node1)
make_material.parse_from(self, material, c2, defs2, surface_node2)
o['contexts'].append(c)
o2['contexts'].append(c2)
self.finalize_shader(o2, defs2, [decal_context])
@ -2832,7 +2836,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
if ob.find_armature() and self.is_bone_animation_enabled(ob) and bpy.data.worlds['Arm'].generate_gpu_skin == True:
defs.append('_Skinning')
# Billboarding
if len(ob.constraints) > 0 and ob.constraints[0].target != None and \
if len(ob.constraints) > 0 and ob.constraints[0].type == 'TRACK_TO' and ob.constraints[0].target != None and \
ob.constraints[0].target.type == 'CAMERA' and ob.constraints[0].mute == False:
defs.append('_Billboard')

View file

@ -10,9 +10,9 @@ import subprocess
import threading
import webbrowser
import write_data
import nodes_logic
import nodes_renderpath
import nodes_world
import make_logic
import make_renderpath
import make_world
import path_tracer
from exporter import ArmoryExporter
import lib.make_datas
@ -42,10 +42,10 @@ def init_armory_props():
if scene.render.engine != 'CYCLES':
scene.render.engine = 'CYCLES'
scene.render.fps = 60 # Default to 60fps for update loop
# Force camera far to at least 100 units for now, to prevent fighting with light far plane
# Force camera far to at least 200 units for now, to prevent fighting with light far plane
for c in bpy.data.cameras:
if c.clip_end < 100:
c.clip_end = 100
if c.clip_end < 200:
c.clip_end = 200
# Use nodes
for w in bpy.data.worlds:
w.use_nodes = True
@ -96,15 +96,15 @@ def export_data(fp, sdk_path, is_play=False):
# Build node trees
# TODO: cache
nodes_logic.buildNodeTrees()
make_logic.buildNodeTrees()
active_worlds = set()
for scene in bpy.data.scenes:
if scene.game_export and scene.world != None and scene.world.name != 'Arm':
active_worlds.add(scene.world)
world_outputs = nodes_world.buildNodeTrees(active_worlds)
linked_assets = nodes_renderpath.buildNodeTrees(shader_references, asset_references, assets_path)
world_outputs = make_world.buildNodeTrees(active_worlds)
linked_assets = make_renderpath.buildNodeTrees(shader_references, asset_references, assets_path)
for wout in world_outputs:
nodes_world.write_output(wout, asset_references, shader_references)
make_world.write_output(wout, asset_references, shader_references)
# Export scene data
assets.embedded_data = sorted(list(set(assets.embedded_data)))
@ -166,7 +166,7 @@ def export_data(fp, sdk_path, is_play=False):
write_data.write_khafilejs(shader_references, asset_references, is_play, export_physics)
# Write Main.hx
write_data.write_main()
write_data.write_main(is_play, play_project.in_viewport)
# Copy ammo.js if necessary
#if target_name == 'html5':
@ -177,50 +177,39 @@ def export_data(fp, sdk_path, is_play=False):
if not os.path.isfile('build/debug-html5/ammo.js'):
shutil.copy(ammojs_path, 'build/debug-html5')
def process_text(text):
if text == None:
return True
# Proof of concept..
text = text.split(' ', 1)
if len(text) > 1:
text = text[1]
cmd = text.split('|')
# Reflect commands from Armory player in Blender
if cmd[0] == '__arm':
if cmd[1] == 'setx':
bpy.context.scene.objects[cmd[2]].location.x = float(cmd[3])
elif cmd[1] == 'select':
bpy.context.object.select = False
bpy.context.scene.objects[cmd[2]].select = True
bpy.context.scene.objects.active = bpy.context.scene.objects[cmd[2]]
return False
return True
def armory_log(text=None):
if process_text(text):
if text == None:
text = ''
print(text)
text = (text[:80] + '..') if len(text) > 80 else text # Limit str size
ArmoryProjectPanel.info_text = text
armory_log.tag_redraw = True
if text == None:
text = ''
print(text)
text = (text[:80] + '..') if len(text) > 80 else text # Limit str size
ArmoryProjectPanel.info_text = text
armory_log.tag_redraw = True
armory_log.tag_redraw = False
def armory_space_log(text):
if process_text(text):
print(text)
text = (text[:80] + '..') if len(text) > 80 else text # Limit str size
space_armory.SPACEARMORY_HT_header.info_text = text
def armory_progress(value):
ArmoryProjectPanel.progress = value
armory_log.tag_redraw = True
def armory_space_log(text=None):
if text == None:
text = ''
print(text)
text = (text[:80] + '..') if len(text) > 80 else text # Limit str size
space_armory.SPACEARMORY_HT_header.info_text = text
def get_kha_target(target_name): # TODO: remove
if target_name == 'macos':
return 'osx'
return target_name
def compile_project(target_name=None, is_publish=False):
def compile_project(target_name=None, is_publish=False, watch=False):
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path
ffmpeg_path = addon_prefs.ffmpeg_path
# Prevent launching the player after compile process finishes
make.play_project.in_viewport = False
# Set build command
if target_name == None:
@ -239,12 +228,16 @@ def compile_project(target_name=None, is_publish=False):
kha_target_name = get_kha_target(target_name)
cmd = [node_path, khamake_path, kha_target_name, '--glsl2']
if ffmpeg_path != '':
cmd.append('--ffmpeg')
cmd.append('"' + ffmpeg_path + '"')
# armory_log("Building, see console...")
if make.play_project.playproc != None or make.play_project.chromium_running:
if make.play_project.chromium_running:
if play_project.compileproc == None: # Already compiling
# Patch running game, stay silent, disable krafix and haxe
cmd.append('--silent')
# cmd.append('--silent')
cmd.append('--noproject')
cmd.append('--haxe')
cmd.append('""')
@ -254,8 +247,10 @@ def compile_project(target_name=None, is_publish=False):
play_project.compileproc = subprocess.Popen(cmd, stderr=subprocess.PIPE)
threading.Timer(0.1, watch_patch).start()
return play_project.compileproc
else:
return None
elif watch == True:
play_project.compileproc = subprocess.Popen(cmd)
threading.Timer(0.1, watch_compile, ['build']).start()
return play_project.compileproc
else:
return subprocess.Popen(cmd)
@ -272,6 +267,11 @@ def build_project(is_play=False):
# Save blend
bpy.ops.wm.save_mainfile()
# Clean log
armory_log()
if utils.with_chromium():
armory_space_log()
# Set camera in active scene
wrd = bpy.data.worlds['Arm']
active_scene = bpy.context.screen.scene if wrd.ArmPlayActiveScene else bpy.data.scenes[wrd.ArmProjectScene]
@ -330,6 +330,9 @@ def build_project(is_play=False):
# Export data
export_data(fp, sdk_path, is_play=is_play)
if play_project.playproc == None:
armory_progress(50)
def stop_project():
if play_project.playproc != None:
play_project.playproc.terminate()
@ -353,15 +356,17 @@ def watch_play():
play_project.playproc_finished = True
armory_log()
def watch_compile(is_publish=False):
def watch_compile(mode):
print('WATCHHHH', mode)
play_project.compileproc.wait()
result = play_project.compileproc.poll()
play_project.compileproc = None
play_project.compileproc_finished = True
if result == 0:
on_compiled(is_publish)
on_compiled(mode)
else:
armory_log('Build failed, check console')
armory_progress(100)
def watch_patch():
play_project.compileproc.wait()
@ -415,7 +420,7 @@ def play_project(self, in_viewport):
# Compile
play_project.compileproc = compile_project(target_name='html5')
threading.Timer(0.1, watch_compile).start()
threading.Timer(0.1, watch_compile, ['play']).start()
play_project.in_viewport = False
play_project.playproc = None
@ -424,6 +429,7 @@ play_project.playproc_finished = False
play_project.compileproc_finished = False
play_project.play_area = None
play_project.chromium_running = False
play_project.last_chromium_running = False
def run_server():
Handler = http.server.SimpleHTTPRequestHandler
@ -433,14 +439,15 @@ def run_server():
except:
print('Server already running')
def on_compiled(is_publish=False):
def on_compiled(mode): # build, play, publish
armory_progress(100)
armory_log()
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path
# Print info
if is_publish:
if mode == 'publish':
target_name = get_kha_target(bpy.data.worlds['Arm'].ArmPublishTarget)
print('Project published')
files_path = utils.get_fp() + '/build/' + target_name
@ -457,7 +464,7 @@ def on_compiled(is_publish=False):
return
# Otherwise launch project
if utils.with_chromium() == False or play_project.in_viewport == False:
elif mode =='play' and (utils.with_chromium() == False or play_project.in_viewport == False):
wrd = bpy.data.worlds['Arm']
if wrd.ArmPlayRuntime == 'Electron':
electron_app_path = './build/electron.js'
@ -485,12 +492,25 @@ def on_compiled(is_publish=False):
def clean_project():
os.chdir(utils.get_fp())
wrd = bpy.data.worlds['Arm']
# Preserve envmaps
if not wrd.ArmCleanEnvmaps:
envmaps_path = 'build/compiled/Assets/envmaps'
if os.path.isdir(envmaps_path):
shutil.move(envmaps_path, '.')
# Remove build and compiled data
if os.path.isdir('build'):
shutil.rmtree('build')
# Move envmaps back
if not wrd.ArmCleanEnvmaps and os.path.isdir('envmaps'):
os.makedirs('build/compiled/Assets')
shutil.move('envmaps', 'build/compiled/Assets')
# Remove compiled nodes
nodes_path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace('.', '/') + '/node/'
nodes_path = 'Sources/' + wrd.ArmProjectPackage.replace('.', '/') + '/node/'
if os.path.isdir(nodes_path):
shutil.rmtree(nodes_path)
@ -505,10 +525,14 @@ def clean_project():
print('Project cleaned')
def publish_project():
# Force minimize data
minimize = bpy.data.worlds['Arm'].ArmMinimize
bpy.data.worlds['Arm'].ArmMinimize = True
clean_project()
build_project()
play_project.compileproc = compile_project(target_name=bpy.data.worlds['Arm'].ArmPublishTarget, is_publish=True)
threading.Timer(0.1, watch_compile, {"is_publish" : True}).start()
threading.Timer(0.1, watch_compile, ['publish']).start()
bpy.data.worlds['Arm'].ArmMinimize = minimize
# Registration
def register():

View file

@ -5,14 +5,6 @@ import os
import json
import write_probes
def register():
pass
#bpy.utils.register_module(__name__)
def unregister():
pass
#bpy.utils.unregister_module(__name__)
def parse_defs(node_group):
rn = get_root_node(node_group)
@ -87,3 +79,11 @@ def get_output_node(node_group, from_node, output_index):
if link.to_node.bl_idname == 'NodeReroute': # Step through reroutes
return findNodeByLinkFrom(node_group, link.to_node, link.to_node.inputs[0])
return link.to_node
def register():
pass
#bpy.utils.register_module(__name__)
def unregister():
pass
#bpy.utils.unregister_module(__name__)

111
blender/make_logic.py Executable file
View file

@ -0,0 +1,111 @@
import bpy
from bpy.types import NodeTree, Node, NodeSocket
from bpy.props import *
import os
import sys
import nodes_logic
# Generating node sources
def buildNodeTrees():
s = bpy.data.filepath.split(os.path.sep)
s.pop()
fp = os.path.sep.join(s)
os.chdir(fp)
# Make sure package dir exists
nodes_path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace(".", "/") + "/node"
if not os.path.exists(nodes_path):
os.makedirs(nodes_path)
# Export node scripts
for node_group in bpy.data.node_groups:
if node_group.bl_idname == 'ArmLogicTreeType': # Build only game trees
node_group.use_fake_user = True # Keep fake references for now
buildNodeTree(node_group)
def buildNodeTree(node_group):
path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace('.', '/') + '/node/'
node_group_name = node_group.name.replace('.', '_').replace(' ', '')
with open(path + node_group_name + '.hx', 'w') as f:
f.write('package ' + bpy.data.worlds['Arm'].ArmProjectPackage + '.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')
# Make sure root node exists
roots = get_root_nodes(node_group)
created_nodes = []
for rn in roots:
name = '_' + rn.name.replace('.', '_').replace(' ', '')
buildNode(node_group, rn, f, created_nodes)
f.write('\n\t\tstart(' + name + ');\n\n')
f.write('\t}\n')
f.write('}\n')
def buildNode(node_group, node, f, created_nodes):
# Get node name
name = '_' + node.name.replace('.', '_').replace(' ', '')
# Check if node already exists
for n in created_nodes:
if n == name:
return name
# Create node
type = node.name.split(".")[0].replace(' ', '') + "Node"
f.write('\t\tvar ' + name + ' = new ' + type + '();\n')
created_nodes.append(name)
# Properties
if hasattr(node, "property0"):
f.write('\t\t' + name + '.property0 = "' + node.property0 + '";\n')
if hasattr(node, "property1"):
f.write('\t\t' + name + '.property1 = "' + node.property1 + '";\n')
if hasattr(node, "property2"):
f.write('\t\t' + name + '.property2 = "' + node.property2 + '";\n')
if hasattr(node, "property3"):
f.write('\t\t' + name + '.property3 = "' + node.property3 + '";\n')
if hasattr(node, "property4"):
f.write('\t\t' + name + '.property4 = "' + node.property4 + '";\n')
# Create inputs
for inp in node.inputs:
# Is linked - find node
inpname = ''
if inp.is_linked:
n = findNodeByLink(node_group, node, inp)
inpname = buildNode(node_group, n, f, created_nodes)
# Not linked - create node with default values
else:
inpname = build_default_node(inp)
# Add input
f.write('\t\t' + name + '.inputs.push(' + inpname + ');\n')
return name
def findNodeByLink(node_group, to_node, inp):
for link in node_group.links:
if link.to_node == to_node and link.to_socket == inp:
if link.from_node.bl_idname == 'NodeReroute': # Step through reroutes
return findNodeByLink(node_group, link.from_node, link.from_node.inputs[0])
return link.from_node
def get_root_nodes(node_group):
roots = []
for n in node_group.nodes:
if len(n.outputs) == 0: # Assume node with no outputs as roots
roots.append(n)
return roots
def build_default_node(inp):
inpname = ''
if inp.type == "VECTOR":
inpname = 'VectorNode.create(' + str(inp.default_value[0]) + ', ' + str(inp.default_value[1]) + ", " + str(inp.default_value[2]) + ')'
elif inp.type == "VALUE":
inpname = 'FloatNode.create(' + str(inp.default_value) + ')'
elif inp.type == 'BOOLEAN':
inpname = 'BoolNode.create(' + str(inp.default_value).lower() + ')'
return inpname

816
blender/make_renderpath.py Executable file
View file

@ -0,0 +1,816 @@
import bpy
from bpy.types import NodeTree, Node, NodeSocket
from bpy.props import *
import os
import sys
import json
import platform
import subprocess
import make_compositor
from utils import to_hex
import assets
import utils
# Handling node data
def reload_blend_data():
if bpy.data.node_groups.get('forward_path') == None:
load_library()
pass
def load_library():
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path
data_path = sdk_path + '/armory/blender/data/data.blend'
with bpy.data.libraries.load(data_path, link=False) as (data_from, data_to):
data_to.node_groups = ['forward_path', 'forward_path_low', 'deferred_path', 'deferred_path_low', 'deferred_path_high', 'hybrid_path', 'vr_path', 'pathtrace_path', 'Armory PBR']
# TODO: cannot use for loop
bpy.data.node_groups['forward_path'].use_fake_user = True
bpy.data.node_groups['forward_path_low'].use_fake_user = True
bpy.data.node_groups['deferred_path'].use_fake_user = True
bpy.data.node_groups['deferred_path_low'].use_fake_user = True
bpy.data.node_groups['deferred_path_high'].use_fake_user = True
bpy.data.node_groups['hybrid_path'].use_fake_user = True
bpy.data.node_groups['vr_path'].use_fake_user = True
bpy.data.node_groups['pathtrace_path'].use_fake_user = True
bpy.data.node_groups['Armory PBR'].use_fake_user = True
def buildNodeTrees(shader_references, asset_references, assets_path):
s = bpy.data.filepath.split(os.path.sep)
s.pop()
fp = os.path.sep.join(s)
os.chdir(fp)
# Make sure Assets dir exists
if not os.path.exists('build/compiled/Assets/renderpaths'):
os.makedirs('build/compiled/Assets/renderpaths')
buildNodeTrees.assets_path = assets_path
buildNodeTrees.linked_assets = []
# Always include
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'brdf.png')
# Export render path for each camera
parsed_paths = []
for cam in bpy.data.cameras:
# if cam.game_export
if cam.renderpath_path not in parsed_paths:
node_group = bpy.data.node_groups[cam.renderpath_path]
buildNodeTree(cam, node_group, shader_references, asset_references)
parsed_paths.append(cam.renderpath_path)
return buildNodeTrees.linked_assets
def buildNodeTree(cam, node_group, shader_references, asset_references):
buildNodeTree.cam = cam
output = {}
dat = {}
output['renderpath_datas'] = [dat]
path = 'build/compiled/Assets/renderpaths/'
node_group_name = node_group.name.replace('.', '_')
rn = get_root_node(node_group)
if rn == None:
return
dat['name'] = node_group_name
# Store main context names
dat['mesh_context'] = buildNodeTree.cam.mesh_context
dat['shadows_context'] = buildNodeTree.cam.shadows_context
dat['render_targets'], dat['depth_buffers'] = preprocess_renderpath(rn, node_group)
dat['stages'] = []
buildNode(dat['stages'], rn, node_group, shader_references, asset_references)
asset_path = path + node_group_name + '.arm'
utils.write_arm(asset_path, output)
assets.add(asset_path)
def make_set_target(stage, node_group, node, currentNode=None, target_index=1, viewport_scale=1.0):
if currentNode == None:
currentNode = node
stage['command'] = 'set_target'
# First param is viewport scale
if len(stage['params']) == 0:
stage['params'].append(viewport_scale)
currentNode = findNodeByLink(node_group, currentNode, currentNode.inputs[target_index])
if currentNode.bl_idname == 'TargetNodeType':
targetId = currentNode.inputs[0].default_value
stage['params'].append(targetId)
# Store current target size
buildNode.last_set_target_w = currentNode.inputs[1].default_value
buildNode.last_set_target_h = currentNode.inputs[2].default_value
elif currentNode.bl_idname == 'GBufferNodeType':
# Set all linked targets
for i in range(0, 5):
if currentNode.inputs[i].is_linked:
make_set_target(stage, node_group, node, currentNode, target_index=i)
elif currentNode.bl_idname == 'NodeReroute':
make_set_target(stage, node_group, node, currentNode, target_index=0)
else: # Framebuffer
targetId = ''
stage['params'].append(targetId)
def make_clear_target(stage, color_val=None, depth_val=None, stencil_val=None):
stage['command'] = 'clear_target'
if color_val != None:
stage['params'].append('color')
if color_val == -1: # Clear to world background color
stage['params'].append('-1')
else:
stage['params'].append(str(to_hex(color_val)))
if depth_val != None:
stage['params'].append('depth')
stage['params'].append(str(depth_val))
if stencil_val != None:
stage['params'].append('stencil')
stage['params'].append(str(stencil_val))
def make_draw_meshes(stage, node_group, node):
stage['command'] = 'draw_meshes'
# Context
context = node.inputs[1].default_value
# Store shadowmap size
if context == buildNodeTree.cam.shadows_context:
bpy.data.worlds['Arm'].shadowmap_size = buildNode.last_set_target_w
stage['params'].append(context)
# Order
order = node.inputs[2].default_value
stage['params'].append(order)
def make_draw_decals(stage, node_group, node, shader_references, asset_references):
stage['command'] = 'draw_decals'
context = node.inputs[1].default_value
stage['params'].append(context) # Context
buildNodeTree.cam.last_decal_context = context
def make_bind_target(stage, node_group, node, constant_name, currentNode=None, target_index=1):
if currentNode == None:
currentNode = node
stage['command'] = 'bind_target'
link = findLink(node_group, currentNode, currentNode.inputs[target_index])
currentNode = link.from_node
if currentNode.bl_idname == 'NodeReroute':
make_bind_target(stage, node_group, node, constant_name, currentNode=currentNode, target_index=0)
elif currentNode.bl_idname == 'GBufferNodeType':
for i in range(0, 5):
if currentNode.inputs[i].is_linked:
targetNode = findNodeByLink(node_group, currentNode, currentNode.inputs[i])
targetId = targetNode.inputs[0].default_value
# if i == 0 and targetNode.inputs[3].default_value == True: # Depth
if targetNode.inputs[3].is_linked: # Depth
db_node = findNodeByLink(node_group, targetNode, targetNode.inputs[3])
db_id = db_node.inputs[0].default_value
stage['params'].append('_' + db_id)
stage['params'].append(constant_name + 'D')
stage['params'].append(targetId) # Color buffer
stage['params'].append(constant_name + str(i))
elif currentNode.bl_idname == 'TargetNodeType':
targetId = currentNode.inputs[0].default_value
stage['params'].append(targetId)
stage['params'].append(constant_name)
elif currentNode.bl_idname == 'DepthBufferNodeType':
targetId = '_' + currentNode.inputs[0].default_value
stage['params'].append(targetId)
stage['params'].append(constant_name)
def make_draw_material_quad(stage, node_group, node, shader_references, asset_references, context_index=1):
stage['command'] = 'draw_material_quad'
material_context = node.inputs[context_index].default_value
stage['params'].append(material_context)
# Include data and shaders
shader_context = node.inputs[context_index].default_value
scon = shader_context.split('/')
dir_name = scon[2]
# No world defs for material passes
data_name = scon[2]
asset_references.append('build/compiled/ShaderDatas/' + dir_name + '/' + data_name + '.arm')
shader_references.append('build/compiled/Shaders/' + dir_name + '/' + data_name)
def make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=1, shader_context=None):
stage['command'] = 'draw_shader_quad'
# Append world defs to get proper context
world_defs = bpy.data.worlds['Arm'].world_defs
if shader_context == None:
shader_context = node.inputs[context_index].default_value
scon = shader_context.split('/')
stage['params'].append(scon[0] + world_defs + '/' + scon[1] + world_defs + '/' + scon[2])
# Include data and shaders
dir_name = scon[0]
# Append world defs
data_name = scon[1] + world_defs
asset_references.append('build/compiled/ShaderDatas/' + dir_name + '/' + data_name + '.arm')
shader_references.append('build/compiled/Shaders/' + dir_name + '/' + data_name)
def make_draw_world(stage, node_group, node, shader_references, asset_references, dome=True):
if dome:
stage['command'] = 'draw_skydome'
else:
stage['command'] = 'draw_material_quad'
# stage['params'].append(wname + '_material/' + wname + '_material/env')
stage['params'].append('_worldMaterial') # Link to active world
# Link assets
if '_EnvClouds' in bpy.data.worlds['Arm'].world_defs:
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise256.png')
assets.add_embedded_data('noise256.png')
def make_draw_compositor(stage, node_group, node, shader_references, asset_references, with_fxaa=False):
scon = 'compositor_pass'
wrd = bpy.data.worlds['Arm']
world_defs = wrd.world_defs
compositor_defs = make_compositor.parse_defs(bpy.data.scenes[0].node_tree) # Thrown in scene 0 for now
# Additional compositor flags
compo_depth = False # Read depth
compo_pos = False # Construct position from depth
if with_fxaa: # FXAA directly in compositor, useful for forward path
compositor_defs += '_CompoFXAA'
if wrd.generate_letterbox:
compositor_defs += '_CompoLetterbox'
if wrd.generate_grain:
compositor_defs += '_CompoGrain'
if bpy.data.scenes[0].cycles.film_exposure != 1.0:
compositor_defs += '_CompoExposure'
if wrd.generate_fog:
compositor_defs += '_CompoFog'
compo_pos = True
if buildNodeTree.cam.cycles.aperture_size > 0.0:
compositor_defs += '_CompoDOF'
compo_depth = True
if compo_pos:
compositor_defs += '_CompoPos'
compo_depth = True
if compo_depth:
compositor_defs += '_CompoDepth'
defs = world_defs + compositor_defs
data_name = scon + defs
stage['command'] = 'draw_shader_quad'
stage['params'].append(data_name + '/' + data_name + '/' + scon)
# Include data and shaders
asset_references.append('build/compiled/ShaderDatas/' + scon + '/' + data_name + '.arm')
shader_references.append('build/compiled/Shaders/' + scon + '/' + data_name)
# Link assets
# buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise256.png')
# assets.add_embedded_data('noise256.png')
def make_call_function(stage, node_group, node):
stage['command'] = 'call_function'
stage['params'].append(node.inputs[1].default_value)
def make_branch_function(stage, node_group, node):
make_call_function(stage, node_group, node)
def process_call_function(stage, stages, node, node_group, shader_references, asset_references):
# Step till merge node
stage['returns_true'] = []
if node.outputs[0].is_linked:
stageNode = findNodeByLinkFrom(node_group, node, node.outputs[0])
buildNode(stage['returns_true'], stageNode, node_group, shader_references, asset_references)
stage['returns_false'] = []
if node.outputs[1].is_linked:
stageNode = findNodeByLinkFrom(node_group, node, node.outputs[1])
margeNode = buildNode(stage['returns_false'], stageNode, node_group, shader_references, asset_references)
# Continue using top level stages after merge node
afterMergeNode = findNodeByLinkFrom(node_group, margeNode, margeNode.outputs[0])
buildNode(stages, afterMergeNode, node_group, shader_references, asset_references)
def make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 5, 7], bind_target_constants=None, shader_context=None, viewport_scale=1.0, with_clear=False):
# Set target
if target_index != None and node.inputs[target_index].is_linked:
stage = {}
stage['params'] = []
make_set_target(stage, node_group, node, target_index=target_index, viewport_scale=viewport_scale)
stages.append(stage)
# Optinal clear
if with_clear:
stage = {}
stage['params'] = []
make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 1.0])
stages.append(stage)
# Bind targets
stage = {}
stage['params'] = []
buildNode.last_bind_target = stage
bind_target_used = False
for i in range(0, len(bind_target_indices)):
index = bind_target_indices[i]
if node.inputs[index].is_linked:
bind_target_used = True
if bind_target_constants == None:
constant_name = node.inputs[index + 1].default_value
else:
constant_name = bind_target_constants[i]
make_bind_target(stage, node_group, node, constant_name, target_index=index)
if bind_target_used:
stages.append(stage)
stage = {}
stage['params'] = []
# Draw quad
make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=2, shader_context=shader_context)
stages.append(stage)
def make_ssao_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 4], bind_target_constants=['gbufferD', 'gbuffer0'], shader_context='ssao_pass/ssao_pass/ssao_pass', viewport_scale=bpy.data.worlds['Arm'].generate_ssao_texture_scale)
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[1, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x', viewport_scale=bpy.data.worlds['Arm'].generate_ssao_texture_scale)
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y')
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise8.png')
assets.add_embedded_data('noise8.png')
def make_ssao_reproject_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 4, 2, 5], bind_target_constants=['gbufferD', 'gbuffer0', 'slast', 'sveloc'], shader_context='ssao_reproject_pass/ssao_reproject_pass/ssao_reproject_pass')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[1, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y')
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise8.png')
assets.add_embedded_data('noise8.png')
def make_apply_ssao_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[4, 5], bind_target_constants=['gbufferD', 'gbuffer0'], shader_context='ssao_pass/ssao_pass/ssao_pass')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=3, bind_target_indices=[2, 5], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 5], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y_blend')
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise8.png')
assets.add_embedded_data('noise8.png')
def make_ssr_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[4, 5, 6], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='ssr_pass/ssr_pass/ssr_pass', viewport_scale=bpy.data.worlds['Arm'].generate_ssr_texture_scale)
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=3, bind_target_indices=[2, 6], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_adaptive_pass/blur_adaptive_pass/blur_adaptive_pass_x', viewport_scale=bpy.data.worlds['Arm'].generate_ssr_texture_scale, with_clear=True) # Have to clear to prevent artefacts, potentially because of viewport scale
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 6], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_adaptive_pass/blur_adaptive_pass/blur_adaptive_pass_y3_blend')
def make_bloom_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[4], bind_target_constants=['tex'], shader_context='bloom_pass/bloom_pass/bloom_pass')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=3, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='blur_gaus_pass/blur_gaus_pass/blur_gaus_pass_x')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3], bind_target_constants=['tex'], shader_context='blur_gaus_pass/blur_gaus_pass/blur_gaus_pass_y_blend')
def make_motion_blur_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3, 4], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='motion_blur_pass/motion_blur_pass/motion_blur_pass')
def make_motion_blur_velocity_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3, 4], bind_target_constants=['tex', 'gbuffer0', 'sveloc'], shader_context='motion_blur_veloc_pass/motion_blur_veloc_pass/motion_blur_veloc_pass')
def make_copy_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='copy_pass/copy_pass/copy_pass')
def make_blend_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='blend_pass/blend_pass/blend_pass')
def make_combine_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3], bind_target_constants=['tex', 'tex2'], shader_context='combine_pass/combine_pass/combine_pass')
def make_blur_basic_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[1], bind_target_constants=['tex'], shader_context='blur_pass/blur_pass/blur_pass_x')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='blur_pass/blur_pass/blur_pass_y')
def make_debug_normals_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='debug_normals_pass/debug_normals_pass/debug_normals_pass')
def make_fxaa_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='fxaa_pass/fxaa_pass/fxaa_pass')
def make_smaa_pass(stages, node_group, node, shader_references, asset_references):
stage = {}
stage['params'] = []
make_set_target(stage, node_group, node, target_index=2)
stages.append(stage)
stage = {}
stage['params'] = []
make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 0.0])
stages.append(stage)
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=None, bind_target_indices=[4], bind_target_constants=['colorTex'], shader_context='smaa_edge_detect/smaa_edge_detect/smaa_edge_detect')
stage = {}
stage['params'] = []
make_set_target(stage, node_group, node, target_index=3)
stages.append(stage)
stage = {}
stage['params'] = []
make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 0.0])
stages.append(stage)
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=None, bind_target_indices=[2], bind_target_constants=['edgesTex'], shader_context='smaa_blend_weight/smaa_blend_weight/smaa_blend_weight')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[4, 3, 5], bind_target_constants=['colorTex', 'blendTex', 'sveloc'], shader_context='smaa_neighborhood_blend/smaa_neighborhood_blend/smaa_neighborhood_blend')
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'smaa_area.png')
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'smaa_search.png')
assets.add_embedded_data('smaa_area.png')
assets.add_embedded_data('smaa_search.png')
def make_taa_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3, 4], bind_target_constants=['tex', 'tex2', 'sveloc'], shader_context='taa_pass/taa_pass/taa_pass')
def make_sss_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 4, 5], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='sss_pass/sss_pass/sss_pass_x')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[3, 4, 5], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='sss_pass/sss_pass/sss_pass_y')
def make_water_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3], bind_target_constants=['tex', 'gbufferD'], shader_context='water_pass/water_pass/water_pass')
def make_deferred_light_pass(stages, node_group, node, shader_references, asset_references):
# make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3], bind_target_constants=['gbuffer', 'shadowMap'], shader_context='deferred_light/deferred_light/deferred_light')
# Draw lamp volume - TODO: properly generate stage
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3], bind_target_constants=['gbuffer', 'shadowMap'], shader_context='deferred_light/deferred_light/deferred_light')
stages[-1]['command'] = 'draw_lamp_volume'
def make_volumetric_light_pass(stages, node_group, node, shader_references, asset_references):
# Draw lamp volume - TODO: properly generate stage
# make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[5, 6], bind_target_constants=['gbufferD', 'shadowMap'], shader_context='volumetric_light/volumetric_light/volumetric_light_blend')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[5, 6], bind_target_constants=['gbufferD', 'shadowMap'], shader_context='volumetric_light/volumetric_light/volumetric_light')
stages[-1]['command'] = 'draw_lamp_volume'
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=3, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x')
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y_blend_add')
def make_deferred_indirect_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3], bind_target_constants=['gbuffer', 'ssaotex'], shader_context='deferred_indirect/deferred_indirect/deferred_indirect')
def make_translucent_resolve_pass(stages, node_group, node, shader_references, asset_references):
make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['gbuffer'], shader_context='translucent_resolve/translucent_resolve/translucent_resolve')
# Returns merge node
def buildNode(stages, node, node_group, shader_references, asset_references):
stage = {}
stage['params'] = []
append_stage = True
if node.bl_idname == 'MergeStagesNodeType':
return node
elif node.bl_idname == 'SetTargetNodeType':
buildNode.last_bind_target = None
make_set_target(stage, node_group, node)
elif node.bl_idname == 'ClearTargetNodeType':
color_val = None
depth_val = None
stencil_val = None
if node.inputs[1].default_value == True:
if node.inputs[2].is_linked: # Assume background color node is linked
color_val = -1 # Clear to world.background_color
else:
color_val = node.inputs[2].default_value
if node.inputs[3].default_value == True:
depth_val = node.inputs[4].default_value
if node.inputs[5].default_value == True:
stencil_val = node.inputs[6].default_value
make_clear_target(stage, color_val=color_val, depth_val=depth_val, stencil_val=stencil_val)
elif node.bl_idname == 'DrawMeshesNodeType':
make_draw_meshes(stage, node_group, node)
elif node.bl_idname == 'DrawDecalsNodeType':
make_draw_decals(stage, node_group, node, shader_references, asset_references)
elif node.bl_idname == 'BindTargetNodeType':
if buildNode.last_bind_target is not None:
stage = buildNode.last_bind_target
append_stage = False
buildNode.last_bind_target = stage
constant_name = node.inputs[2].default_value
make_bind_target(stage, node_group, node, constant_name)
elif node.bl_idname == 'DrawMaterialQuadNodeType':
make_draw_material_quad(stage, node_group, node, shader_references, asset_references)
elif node.bl_idname == 'DrawQuadNodeType':
make_draw_quad(stage, node_group, node, shader_references, asset_references)
elif node.bl_idname == 'DrawWorldNodeType':
# Bind depth for quad
# if node.inputs[1].is_linked:
# stage = {}
# stage['params'] = []
# buildNode.last_bind_target = stage
# if node.inputs[1].is_linked:
# make_bind_target(stage, node_group, node, target_index=1, constant_name='gbufferD')
# stages.append(stage)
stage = {}
stage['params'] = []
# Draw quad
# make_draw_world(stage, node_group, node, shader_references, asset_references, dome=False)
# Draw dome
make_draw_world(stage, node_group, node, shader_references, asset_references, dome=True)
elif node.bl_idname == 'DrawCompositorNodeType' or node.bl_idname == 'DrawCompositorWithFXAANodeType':
# Set target
if node.inputs[1].is_linked:
make_set_target(stage, node_group, node)
stages.append(stage)
# Bind targets
if node.inputs[2].is_linked or node.inputs[3].is_linked or node.inputs[4].is_linked:
stage = {}
stage['params'] = []
buildNode.last_bind_target = stage
if node.inputs[2].is_linked:
make_bind_target(stage, node_group, node, target_index=2, constant_name='tex')
if node.inputs[3].is_linked:
make_bind_target(stage, node_group, node, target_index=3, constant_name='gbufferD')
if node.inputs[4].is_linked:
make_bind_target(stage, node_group, node, target_index=4, constant_name='gbuffer0')
stages.append(stage)
# Draw quad
stage = {}
stage['params'] = []
with_fxaa = node.bl_idname == 'DrawCompositorWithFXAANodeType'
make_draw_compositor(stage, node_group, node, shader_references, asset_references, with_fxaa=with_fxaa)
elif node.bl_idname == 'BranchFunctionNodeType':
make_branch_function(stage, node_group, node)
stages.append(stage)
process_call_function(stage, stages, node, node_group, shader_references, asset_references)
return
elif node.bl_idname == 'LoopStagesNodeType':
# Just repeats the commands
append_stage = False
if node.outputs[1].is_linked:
count = node.inputs[2].default_value
for i in range(0, count):
loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1])
buildNode(stages, loopNode, node_group, shader_references, asset_references)
elif node.bl_idname == 'LoopLampsNodeType':
append_stage = False
stage['command'] = 'loop_lamps'
stages.append(stage)
stage['returns_true'] = []
if node.outputs[1].is_linked:
loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1])
buildNode(stage['returns_true'], loopNode, node_group, shader_references, asset_references)
elif node.bl_idname == 'DrawStereoNodeType':
append_stage = False
stage['command'] = 'draw_stereo'
stages.append(stage)
stage['returns_true'] = []
if node.outputs[1].is_linked:
loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1])
buildNode(stage['returns_true'], loopNode, node_group, shader_references, asset_references)
elif node.bl_idname == 'CallFunctionNodeType':
make_call_function(stage, node_group, node)
elif node.bl_idname == 'QuadPassNodeType':
make_quad_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'SSAOPassNodeType':
make_ssao_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'SSAOReprojectPassNodeType':
make_ssao_reproject_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'ApplySSAOPassNodeType':
make_apply_ssao_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'SSRPassNodeType':
make_ssr_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'BloomPassNodeType':
make_bloom_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'MotionBlurPassNodeType':
make_motion_blur_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'MotionBlurVelocityPassNodeType':
make_motion_blur_velocity_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'CopyPassNodeType':
make_copy_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'BlendPassNodeType':
make_blend_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'CombinePassNodeType':
make_combine_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'BlurBasicPassNodeType':
make_blur_basic_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'DebugNormalsPassNodeType':
make_debug_normals_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'FXAAPassNodeType':
make_fxaa_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'SMAAPassNodeType':
make_smaa_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'TAAPassNodeType':
make_taa_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'SSSPassNodeType':
make_sss_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'WaterPassNodeType':
make_water_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'DeferredLightPassNodeType':
make_deferred_light_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'DeferredIndirectPassNodeType':
make_deferred_indirect_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'VolumetricLightPassNodeType':
make_volumetric_light_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
elif node.bl_idname == 'TranslucentResolvePassNodeType':
make_translucent_resolve_pass(stages, node_group, node, shader_references, asset_references)
append_stage = False
if append_stage:
stages.append(stage)
# Build next stage
if node.outputs[0].is_linked:
stageNode = findNodeByLinkFrom(node_group, node, node.outputs[0])
buildNode(stages, stageNode, node_group, shader_references, asset_references)
# Used to merge bind target nodes into one stage
buildNode.last_bind_target = None
# Used to determine shadowmap size
buildNode.last_set_target_w = 0
buildNode.last_set_target_h = 0
def findNodeByLink(node_group, to_node, inp):
for link in node_group.links:
if link.to_node == to_node and link.to_socket == inp:
return link.from_node
def findLink(node_group, to_node, inp):
for link in node_group.links:
if link.to_node == to_node and link.to_socket == inp:
return link
def findNodeByLinkFrom(node_group, from_node, outp):
for link in node_group.links:
if link.from_node == from_node and link.from_socket == outp:
return link.to_node
def get_root_node(node_group):
# Find first node linked to begin node
rn = None
for n in node_group.nodes:
if n.bl_idname == 'BeginNodeType':
# Store contexts
buildNodeTree.cam.renderpath_id = n.inputs[0].default_value
mesh_contexts = n.inputs[1].default_value.split(',')
buildNodeTree.cam.mesh_context = mesh_contexts[0]
if len(mesh_contexts) > 1:
buildNodeTree.cam.mesh_context_empty = mesh_contexts[1]
buildNodeTree.cam.shadows_context = n.inputs[2].default_value
buildNodeTree.cam.translucent_context = n.inputs[3].default_value
buildNodeTree.cam.overlay_context = n.inputs[4].default_value
if n.inputs[5].default_value == False: # No HDR space lighting, append def
bpy.data.worlds['Arm'].world_defs += '_LDR'
rn = findNodeByLinkFrom(node_group, n, n.outputs[0])
break
return rn
def preprocess_renderpath(root_node, node_group):
render_targets = []
depth_buffers = []
preprocess_renderpath.velocity_def_added = False
buildNodeTree.cam.renderpath_passes = ''
traverse_renderpath(root_node, node_group, render_targets, depth_buffers)
return render_targets, depth_buffers
def traverse_renderpath(node, node_group, render_targets, depth_buffers):
# Gather linked draw geometry contexts
if node.bl_idname == 'DrawMeshesNodeType':
if buildNodeTree.cam.renderpath_passes != '':
buildNodeTree.cam.renderpath_passes += '_' # Separator
buildNodeTree.cam.renderpath_passes += node.inputs[1].default_value
# Gather defs from linked nodes
if node.bl_idname == 'TAAPassNodeType' or node.bl_idname == 'MotionBlurVelocityPassNodeType' or node.bl_idname == 'SSAOReprojectPassNodeType':
if preprocess_renderpath.velocity_def_added == False:
assets.add_khafile_def('WITH_VELOC')
bpy.data.worlds['Arm'].world_defs += '_Veloc'
preprocess_renderpath.velocity_def_added = True
if node.bl_idname == 'TAAPassNodeType':
assets.add_khafile_def('WITH_TAA')
# bpy.data.worlds['Arm'].world_defs += '_TAA'
elif node.bl_idname == 'SMAAPassNodeType':
bpy.data.worlds['Arm'].world_defs += '_SMAA'
elif node.bl_idname == 'SSAOPassNodeType' or node.bl_idname == 'ApplySSAOPassNodeType' or node.bl_idname == 'SSAOReprojectPassNodeType':
if bpy.data.worlds['Arm'].generate_ssao: # SSAO enabled
bpy.data.worlds['Arm'].world_defs += '_SSAO'
elif node.bl_idname == 'DrawStereoNodeType':
assets.add_khafile_def('WITH_VR')
bpy.data.worlds['Arm'].world_defs += '_VR'
# Collect render targets
if node.bl_idname == 'SetTargetNodeType' or node.bl_idname == 'QuadPassNodeType' or node.bl_idname == 'DrawCompositorNodeType' or node.bl_idname == 'DrawCompositorWithFXAANodeType':
if node.inputs[1].is_linked:
tnode = findNodeByLink(node_group, node, node.inputs[1])
parse_render_target(tnode, node_group, render_targets, depth_buffers)
# Traverse loops
elif node.bl_idname == 'LoopStagesNodeType' or node.bl_idname == 'LoopLampsNodeType' or node.bl_idname == 'DrawStereoNodeType':
if node.outputs[1].is_linked:
loop_node = findNodeByLinkFrom(node_group, node, node.outputs[1])
traverse_renderpath(loop_node, node_group, render_targets, depth_buffers)
# Prebuilt
elif node.bl_idname == 'MotionBlurPassNodeType' or node.bl_idname == 'MotionBlurVelocityPassNodeType' or node.bl_idname == 'CopyPassNodeType' or node.bl_idname == 'BlendPassNodeType' or node.bl_idname == 'CombinePassNodeType' or node.bl_idname == 'DebugNormalsPassNodeType' or node.bl_idname == 'FXAAPassNodeType' or node.bl_idname == 'TAAPassNodeType' or node.bl_idname == 'WaterPassNodeType' or node.bl_idname == 'DeferredLightPassNodeType' or node.bl_idname == 'DeferredIndirectPassNodeType' or node.bl_idname == 'VolumetricLightPassNodeType' or node.bl_idname == 'TranslucentResolvePassNodeType':
if node.inputs[1].is_linked:
tnode = findNodeByLink(node_group, node, node.inputs[1])
parse_render_target(tnode, node_group, render_targets, depth_buffers)
elif node.bl_idname == 'SSRPassNodeType' or node.bl_idname == 'ApplySSAOPassNodeType' or node.bl_idname == 'BloomPassNodeType' or node.bl_idname == 'SMAAPassNodeType':
for i in range(1, 4):
if node.inputs[i].is_linked:
tnode = findNodeByLink(node_group, node, node.inputs[i])
parse_render_target(tnode, node_group, render_targets, depth_buffers)
elif node.bl_idname == 'SSAOPassNodeType' or node.bl_idname == 'SSAOReprojectPassNodeType' or node.bl_idname == 'SSSPassNodeType' or node.bl_idname == 'BlurBasicPassNodeType':
for i in range(1, 3):
if node.inputs[i].is_linked:
tnode = findNodeByLink(node_group, node, node.inputs[i])
parse_render_target(tnode, node_group, render_targets, depth_buffers)
# Next stage
if node.outputs[0].is_linked:
stagenode = findNodeByLinkFrom(node_group, node, node.outputs[0])
traverse_renderpath(stagenode, node_group, render_targets, depth_buffers)
def parse_render_target(node, node_group, render_targets, depth_buffers):
if node.bl_idname == 'NodeReroute':
tnode = findNodeByLink(node_group, node, node.inputs[0])
parse_render_target(tnode, node_group, render_targets, depth_buffers)
elif node.bl_idname == 'TargetNodeType':
# Target already exists
id = node.inputs[0].default_value
for t in render_targets:
if t['name'] == id:
return
depth_buffer_id = None
if node.inputs[3].is_linked:
# Find depth buffer
depth_node = findNodeByLink(node_group, node, node.inputs[3])
depth_buffer_id = depth_node.inputs[0].default_value
# Append depth buffer
found = False
for db in depth_buffers:
if db['name'] == depth_buffer_id:
found = True
break
if found == False:
db = {}
db['name'] = depth_buffer_id
db['stencil_buffer'] = depth_node.inputs[1].default_value
depth_buffers.append(db)
# Get scale
scale = 1.0
if node.inputs[1].is_linked:
size_node = findNodeByLink(node_group, node, node.inputs[1])
while size_node.bl_idname == 'NodeReroute': # Step through reroutes
size_node = findNodeByLink(node_group, size_node, size_node.inputs[0])
scale = size_node.inputs[0].default_value
# Append target
target = make_render_target(node, scale, depth_buffer_id=depth_buffer_id)
render_targets.append(target)
elif node.bl_idname == 'GBufferNodeType':
for i in range(0, 5):
if node.inputs[i].is_linked:
n = findNodeByLink(node_group, node, node.inputs[i])
parse_render_target(n, node_group, render_targets, depth_buffers)
def make_render_target(n, scale, depth_buffer_id=None):
target = {}
target['name'] = n.inputs[0].default_value
target['width'] = n.inputs[1].default_value
target['height'] = n.inputs[2].default_value
target['format'] = n.inputs[4].default_value
if n.inputs[5].default_value:
target['ping_pong'] = True
if scale != 1.0:
target['scale'] = scale
if depth_buffer_id != None:
target['depth_buffer'] = depth_buffer_id
return target

View file

@ -7,14 +7,6 @@ import write_probes
import assets
import utils
def register():
pass
#bpy.utils.register_module(__name__)
def unregister():
pass
#bpy.utils.unregister_module(__name__)
def find_node(node_group, to_node, target_socket):
for link in node_group.links:
if link.to_node == to_node and link.to_socket == target_socket:
@ -203,7 +195,7 @@ def parse_color(world, node, context, envmap_strength_const):
# Irradiance json file name
base_name = utils.safe_filename(world.name)
world.world_envtex_name = base_name
world.world_envtex_name = base_name + '.hdr' # Fake extension
write_probes.write_sky_irradiance(base_name)

View file

@ -48,6 +48,7 @@ class TimeNode(Node, ArmLogicTreeNode):
self.outputs.new('NodeSocketFloat', "Time")
class VectorNode(Node, ArmLogicTreeNode):
'''Vector node'''
bl_idname = 'VectorNodeType'
# Label for nice name display
bl_label = 'Vector'
@ -62,6 +63,7 @@ class VectorNode(Node, ArmLogicTreeNode):
self.outputs.new('NodeSocketVector', "Vector")
class ScaleValueNode(Node, ArmLogicTreeNode):
'''Scale value node'''
bl_idname = 'ScaleValueNodeType'
bl_label = 'ScaleValue'
bl_icon = 'CURVE_PATH'
@ -74,6 +76,7 @@ class ScaleValueNode(Node, ArmLogicTreeNode):
self.outputs.new('NodeSocketFloat', "Value")
class SineNode(Node, ArmLogicTreeNode):
'''Sine node'''
bl_idname = 'SineNodeType'
bl_label = 'Sine'
bl_icon = 'CURVE_PATH'
@ -84,6 +87,7 @@ class SineNode(Node, ArmLogicTreeNode):
self.outputs.new('NodeSocketFloat', "Value")
class ThisNode(Node, ArmLogicTreeNode):
'''This node'''
bl_idname = 'ThisNodeType'
bl_label = 'This'
bl_icon = 'GAME'
@ -92,6 +96,7 @@ class ThisNode(Node, ArmLogicTreeNode):
self.outputs.new('NodeSocketShader', "Target")
class PickerNode(Node, ArmLogicTreeNode):
'''Picker node'''
bl_idname = 'PickerNodeType'
bl_label = 'Picker'
bl_icon = 'GAME'
@ -104,6 +109,7 @@ class PickerNode(Node, ArmLogicTreeNode):
layout.prop_search(self, "property0", context.scene, "objects", text = "")
class SetTransformNode(Node, ArmLogicTreeNode):
'''Set transform node'''
bl_idname = 'SetTransformNodeType'
bl_label = 'Set Transform'
bl_icon = 'GAME'
@ -113,6 +119,7 @@ class SetTransformNode(Node, ArmLogicTreeNode):
self.inputs.new('NodeSocketShader', "Transform")
class SetVisibleNode(Node, ArmLogicTreeNode):
'''Set visible node'''
bl_idname = 'SetVisibleNodeType'
bl_label = 'Set Visible'
bl_icon = 'GAME'
@ -122,6 +129,7 @@ class SetVisibleNode(Node, ArmLogicTreeNode):
self.inputs.new('NodeSocketShader', "Bool")
class GreaterThanNode(Node, ArmLogicTreeNode):
'''Greater than node'''
bl_idname = 'GreaterThanNodeType'
bl_label = 'Greater Than'
bl_icon = 'GAME'
@ -186,108 +194,3 @@ def register():
def unregister():
nodeitems_utils.unregister_node_categories("ARM_LOGIC_NODES")
bpy.utils.unregister_module(__name__)
# Generating node sources
def buildNodeTrees():
s = bpy.data.filepath.split(os.path.sep)
s.pop()
fp = os.path.sep.join(s)
os.chdir(fp)
# Make sure package dir exists
nodes_path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace(".", "/") + "/node"
if not os.path.exists(nodes_path):
os.makedirs(nodes_path)
# Export node scripts
for node_group in bpy.data.node_groups:
if node_group.bl_idname == 'ArmLogicTreeType': # Build only game trees
node_group.use_fake_user = True # Keep fake references for now
buildNodeTree(node_group)
def buildNodeTree(node_group):
path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace('.', '/') + '/node/'
node_group_name = node_group.name.replace('.', '_').replace(' ', '')
with open(path + node_group_name + '.hx', 'w') as f:
f.write('package ' + bpy.data.worlds['Arm'].ArmProjectPackage + '.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')
# Make sure root node exists
roots = get_root_nodes(node_group)
created_nodes = []
for rn in roots:
name = '_' + rn.name.replace('.', '_').replace(' ', '')
buildNode(node_group, rn, f, created_nodes)
f.write('\n\t\tstart(' + name + ');\n\n')
f.write('\t}\n')
f.write('}\n')
def buildNode(node_group, node, f, created_nodes):
# Get node name
name = '_' + node.name.replace('.', '_').replace(' ', '')
# Check if node already exists
for n in created_nodes:
if n == name:
return name
# Create node
type = node.name.split(".")[0].replace(' ', '') + "Node"
f.write('\t\tvar ' + name + ' = new ' + type + '();\n')
created_nodes.append(name)
# Properties
if hasattr(node, "property0"):
f.write('\t\t' + name + '.property0 = "' + node.property0 + '";\n')
if hasattr(node, "property1"):
f.write('\t\t' + name + '.property1 = "' + node.property1 + '";\n')
if hasattr(node, "property2"):
f.write('\t\t' + name + '.property2 = "' + node.property2 + '";\n')
if hasattr(node, "property3"):
f.write('\t\t' + name + '.property3 = "' + node.property3 + '";\n')
if hasattr(node, "property4"):
f.write('\t\t' + name + '.property4 = "' + node.property4 + '";\n')
# Create inputs
for inp in node.inputs:
# Is linked - find node
inpname = ''
if inp.is_linked:
n = findNodeByLink(node_group, node, inp)
inpname = buildNode(node_group, n, f, created_nodes)
# Not linked - create node with default values
else:
inpname = build_default_node(inp)
# Add input
f.write('\t\t' + name + '.inputs.push(' + inpname + ');\n')
return name
def findNodeByLink(node_group, to_node, inp):
for link in node_group.links:
if link.to_node == to_node and link.to_socket == inp:
if link.from_node.bl_idname == 'NodeReroute': # Step through reroutes
return findNodeByLink(node_group, link.from_node, link.from_node.inputs[0])
return link.from_node
def get_root_nodes(node_group):
roots = []
for n in node_group.nodes:
if len(n.outputs) == 0: # Assume node with no outputs as roots
roots.append(n)
return roots
def build_default_node(inp):
inpname = ''
if inp.type == "VECTOR":
inpname = 'VectorNode.create(' + str(inp.default_value[0]) + ', ' + str(inp.default_value[1]) + ", " + str(inp.default_value[2]) + ')'
elif inp.type == "VALUE":
inpname = 'FloatNode.create(' + str(inp.default_value) + ')'
elif inp.type == 'BOOLEAN':
inpname = 'BoolNode.create(' + str(inp.default_value).lower() + ')'
return inpname

File diff suppressed because it is too large Load diff

View file

@ -3,11 +3,11 @@ import bpy
import os
import json
import subprocess
import nodes_renderpath
import make_renderpath
from bpy.types import Menu, Panel, UIList
from bpy.props import *
from traits_clip import *
from traits_action import *
from props_traits_clip import *
from props_traits_action import *
import utils
import make
import space_armory
@ -18,6 +18,25 @@ try:
except ImportError:
pass
def parse_operator(text):
if text == None:
return
# Proof of concept..
text = text.split(' ', 1)
if len(text) > 1:
text = text[1]
cmd = text.split('|')
# Reflect commands from Armory player in Blender
if cmd[0] == '__arm':
if cmd[1] == 'quit':
bpy.ops.arm.space_stop('EXEC_DEFAULT')
elif cmd[1] == 'setx':
bpy.context.scene.objects[cmd[2]].location.x = float(cmd[3])
elif cmd[1] == 'select':
bpy.context.object.select = False
bpy.context.scene.objects[cmd[2]].select = True
bpy.context.scene.objects.active = bpy.context.scene.objects[cmd[2]]
def on_scene_update_post(context):
global last_time
@ -25,16 +44,21 @@ def on_scene_update_post(context):
last_time = time.time()
# Tag redraw if playing in space_armory
make.play_project.last_chromium_running = make.play_project.chromium_running
make.play_project.chromium_running = False
if space_armory.SPACEARMORY_HT_header.is_paused == False:
for area in bpy.context.screen.areas:
if area.type == 'VIEW_GAME':
area.tag_redraw()
make.play_project.chromium_running = True
for area in bpy.context.screen.areas:
if area.type == 'VIEW_GAME':
make.play_project.chromium_running = True
bgame.draw()
# Have to update chromium one more time before exit, to prevent 'AudioSyncReader::Read timed out' warnings
if make.play_project.chromium_running == False:
if make.play_project.last_chromium_running:
bgame.draw()
# Auto patch on every operator change
ops = bpy.context.window_manager.operators
if (make.play_project.playproc != None or make.play_project.chromium_running) and \
if make.play_project.chromium_running and \
bpy.data.worlds['Arm'].ArmPlayLivePatch and \
bpy.data.worlds['Arm'].ArmPlayAutoBuild and \
len(ops) > 0 and \
@ -72,6 +96,9 @@ def on_scene_update_post(context):
# Read chromium console
if bgame.get_console_updated() == 1:
make.armory_space_log(bgame.get_console())
# Read operator console
if bgame.get_operator_updated() == 1:
parse_operator(bgame.get_operator())
break
# New output has been logged
@ -83,7 +110,7 @@ def on_scene_update_post(context):
break
# Player finished, redraw play buttons
if make.play_project.playproc_finished:
if make.play_project.playproc_finished or make.play_project.compileproc_finished:
make.play_project.playproc_finished = False
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D' or area.type == 'PROPERTIES':
@ -92,6 +119,9 @@ def on_scene_update_post(context):
# Compilation finished
if make.play_project.compileproc_finished:
make.play_project.compileproc_finished = False
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D' or area.type == 'PROPERTIES':
area.tag_redraw()
# Notify embedded player
if make.play_project.chromium_running:
bgame.call_js('armory.Scene.patch();')
@ -131,24 +161,29 @@ def invalidate_mesh_data(self, context):
if os.path.isdir(fp + '/build/compiled/Assets/meshes'):
shutil.rmtree(fp + '/build/compiled/Assets/meshes')
def get_arm_progress(self):
return ArmoryProjectPanel.progress
def initProperties():
# For project
bpy.types.World.ArmVersion = StringProperty(name = "ArmVersion", default="")
bpy.types.World.ArmProgress = bpy.props.FloatProperty(name="Progress", description="Current build progress", default=100.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, subtype='PERCENTAGE', get=get_arm_progress)
bpy.types.World.ArmVersion = StringProperty(name="ArmVersion", description="Armory SDK version", default="")
target_prop = EnumProperty(
items = [('html5', 'HTML5', 'html5'),
('windows', 'Windows', 'windows'),
items = [('html5', 'HTML5', 'html5'),
('windows', 'Windows', 'windows'),
('macos', 'MacOS', 'macos'),
('linux', 'Linux', 'linux'),
('linux', 'Linux', 'linux'),
('ios', 'iOS', 'ios'),
('android-native', 'Android', 'android-native')],
name = "Target", default='html5')
name="Target", default='html5',
description='Build paltform')
bpy.types.World.ArmProjectTarget = target_prop
bpy.types.World.ArmPublishTarget = target_prop
bpy.types.World.ArmProjectName = StringProperty(name = "Name", default="ArmoryGame")
bpy.types.World.ArmProjectPackage = StringProperty(name = "Package", default="game")
bpy.types.World.ArmPlayActiveScene = BoolProperty(name="Play Active Scene", default=True)
bpy.types.World.ArmProjectScene = StringProperty(name = "Scene")
bpy.types.World.ArmProjectSamplesPerPixel = IntProperty(name = "Samples per Pixel", default=1)
bpy.types.World.ArmProjectName = StringProperty(name="Name", description="Exported project name", default="ArmoryProject")
bpy.types.World.ArmProjectPackage = StringProperty(name="Package", description="Package name for scripts", default="arm")
bpy.types.World.ArmPlayActiveScene = BoolProperty(name="Play Active Scene", description="Load currently edited scene when launching player", default=True)
bpy.types.World.ArmProjectScene = StringProperty(name="Scene", description="Scene to load when launching player")
bpy.types.World.ArmProjectSamplesPerPixel = IntProperty(name="Samples per Pixel", description="MSAA samples usable for render paths drawing directly to framebuffer", default=1)
bpy.types.World.ArmPhysics = EnumProperty(
items = [('Disabled', 'Disabled', 'Disabled'),
('Bullet', 'Bullet', 'Bullet')],
@ -157,32 +192,35 @@ def initProperties():
items = [('Disabled', 'Disabled', 'Disabled'),
('Recast', 'Recast', 'Recast')],
name = "Navigation", default='Disabled')
bpy.types.World.ArmKhafile = StringProperty(name = "Khafile")
bpy.types.World.ArmMinimize = BoolProperty(name="Minimize Data", default=True, update=invalidate_compiled_data)
bpy.types.World.ArmOptimizeMesh = BoolProperty(name="Optimize Meshes", default=False, update=invalidate_mesh_data)
bpy.types.World.ArmSampledAnimation = BoolProperty(name="Sampled Animation", default=False, update=invalidate_compiled_data)
bpy.types.World.ArmDeinterleavedBuffers = BoolProperty(name="Deinterleaved Buffers", default=False)
bpy.types.World.ArmExportHideRender = BoolProperty(name="Export Hidden Renders", default=False)
bpy.types.World.ArmSpawnAllLayers = BoolProperty(name="Spawn All Layers", default=False)
bpy.types.World.ArmCacheShaders = BoolProperty(name="Cache Shaders", default=True, update=invalidate_shader_cache)
bpy.types.World.ArmPlayLivePatch = BoolProperty(name="Live Patching", default=True)
bpy.types.World.ArmPlayAutoBuild = BoolProperty(name="Auto Build", default=True)
bpy.types.World.ArmPlayViewportCamera = BoolProperty(name="Viewport Camera", default=False)
bpy.types.World.ArmKhafile = StringProperty(name = "Khafile", description="Source appended to khafile.js")
bpy.types.World.ArmMinimize = BoolProperty(name="Minimize Data", description="Export scene data in binary", default=True, update=invalidate_compiled_data)
bpy.types.World.ArmOptimizeMesh = BoolProperty(name="Optimize Meshes", description="Export more efficient geometry indices, can prolong build times", default=False, update=invalidate_mesh_data)
bpy.types.World.ArmSampledAnimation = BoolProperty(name="Sampled Animation", description="Export object animation as raw matrices", default=False, update=invalidate_compiled_data)
bpy.types.World.ArmDeinterleavedBuffers = BoolProperty(name="Deinterleaved Buffers", description="Use deinterleaved vertex buffers", default=False)
bpy.types.World.ArmExportHideRender = BoolProperty(name="Export Hidden Renders", description="Export hidden objects", default=False)
bpy.types.World.ArmSpawnAllLayers = BoolProperty(name="Spawn All Layers", description="Spawn objects from all scene layers", default=False)
bpy.types.World.ArmPlayAdvanced = BoolProperty(name="Advanced", default=False)
bpy.types.World.ArmBuildAdvanced = BoolProperty(name="Advanced", default=False)
bpy.types.World.ArmProjectAdvanced = BoolProperty(name="Advanced", default=False)
bpy.types.World.ArmObjectAdvanced = BoolProperty(name="Advanced", default=False)
bpy.types.World.ArmCacheShaders = BoolProperty(name="Cache Shaders", description="Do not rebuild existing shaders", default=True, update=invalidate_shader_cache)
bpy.types.World.ArmCleanEnvmaps = BoolProperty(name="Clean Envmaps", description="Remove prefiltered maps when cleaning project", default=True)
bpy.types.World.ArmPlayLivePatch = BoolProperty(name="Live Patching", description="Sync running player data to Blender", default=True)
bpy.types.World.ArmPlayAutoBuild = BoolProperty(name="Auto Build", description="Rebuild scene on operator changes", default=True)
bpy.types.World.ArmPlayViewportCamera = BoolProperty(name="Viewport Camera", description="Start player at viewport camera position", default=False)
bpy.types.World.ArmPlayViewportNavigation = EnumProperty(
items = [('None', 'None', 'None'),
('Walk', 'Walk', 'Walk')],
name = "Navigation", default='Walk')
bpy.types.World.ArmPlayConsole = BoolProperty(name="Debug Console", default=False)
bpy.types.World.ArmPlayDeveloperTools = BoolProperty(name="Developer Tools", default=False)
items=[('None', 'None', 'None'),
('Walk', 'Walk', 'Walk')],
name="Navigation", description="Enable camera controls", default='Walk')
bpy.types.World.ArmPlayConsole = BoolProperty(name="Debug Console", description="Show inspector in player", default=False)
bpy.types.World.ArmPlayDeveloperTools = BoolProperty(name="Developer Tools", description="Show chromium developer tools in player", default=False)
bpy.types.World.ArmPlayRuntime = EnumProperty(
items = [('Electron', 'Electron', 'Electron'),
('Browser', 'Browser', 'Browser')],
# ('Native', 'Native', 'Native')],
#('Krom', 'Krom', 'Krom')],
name = "Runtime", default='Electron')
items=[('Electron', 'Electron', 'Electron'),
('Browser', 'Browser', 'Browser')],
name="Runtime", description="Player runtime used when launching in new window", default='Electron')
# For object
bpy.types.Object.instanced_children = bpy.props.BoolProperty(name="Instanced Children", default=False)
bpy.types.Object.instanced_children = bpy.props.BoolProperty(name="Instanced Children", description="Use instaced rendering", default=False)
bpy.types.Object.instanced_children_loc_x = bpy.props.BoolProperty(name="X", default=True)
bpy.types.Object.instanced_children_loc_y = bpy.props.BoolProperty(name="Y", default=True)
bpy.types.Object.instanced_children_loc_z = bpy.props.BoolProperty(name="Z", default=True)
@ -194,52 +232,55 @@ def initProperties():
bpy.types.Object.instanced_children_scale_z = bpy.props.BoolProperty(name="Z", default=False)
bpy.types.Object.override_material = bpy.props.BoolProperty(name="Override Material", default=False)
bpy.types.Object.override_material_name = bpy.props.StringProperty(name="Name", default="")
bpy.types.Object.game_export = bpy.props.BoolProperty(name="Export", default=True)
bpy.types.Object.game_visible = bpy.props.BoolProperty(name="Visible", default=True)
bpy.types.Object.game_export = bpy.props.BoolProperty(name="Export", description="Export object data", default=True)
bpy.types.Object.game_visible = bpy.props.BoolProperty(name="Visible", description="Render this object", default=True)
bpy.types.Object.spawn = bpy.props.BoolProperty(name="Spawn", description="Auto-add this object when creating scene", default=True)
bpy.types.Object.mobile = bpy.props.BoolProperty(name="Mobile", description="Object moves during gameplay", default=True)
# - Clips
bpy.types.Object.bone_animation_enabled = bpy.props.BoolProperty(name="Bone Animation", default=True)
bpy.types.Object.object_animation_enabled = bpy.props.BoolProperty(name="Object Animation", default=True)
bpy.types.Object.edit_tracks_prop = bpy.props.BoolProperty(name="Edit Clips", description="A name for this item", default=False)
bpy.types.Object.start_track_name_prop = bpy.props.StringProperty(name="Start Track", description="A name for this item", default="")
bpy.types.Object.bone_animation_enabled = bpy.props.BoolProperty(name="Bone Animation", description="Enable skinning", default=True)
bpy.types.Object.object_animation_enabled = bpy.props.BoolProperty(name="Object Animation", description="Enable timeline animation", default=True)
bpy.types.Object.edit_tracks_prop = bpy.props.BoolProperty(name="Edit Clips", description="Manually set animation frames", default=False)
bpy.types.Object.start_track_name_prop = bpy.props.StringProperty(name="Start Track", description="Play this track by default", default="")
bpy.types.Object.my_cliptraitlist = bpy.props.CollectionProperty(type=ListClipTraitItem)
bpy.types.Object.cliptraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0)
bpy.types.Object.cliptraitlist_index = bpy.props.IntProperty(name="Clip index", default=0)
# - Actions
bpy.types.Object.edit_actions_prop = bpy.props.BoolProperty(name="Edit Actions", description="A name for this item", default=False)
bpy.types.Object.start_action_name_prop = bpy.props.StringProperty(name="Start Action", description="A name for this item", default="")
bpy.types.Object.edit_actions_prop = bpy.props.BoolProperty(name="Edit Actions", description="Manually set used actions", default=False)
bpy.types.Object.start_action_name_prop = bpy.props.StringProperty(name="Start Action", description="Play this action by default", default="")
# For speakers
bpy.types.Speaker.loop = bpy.props.BoolProperty(name="Loop", description="Loop this sound", default=False)
bpy.types.Speaker.stream = bpy.props.BoolProperty(name="Stream", description="Stream this sound", default=False)
# For mesh
bpy.types.Mesh.mesh_cached = bpy.props.BoolProperty(name="Mesh Cached", default=False)
bpy.types.Mesh.mesh_cached_verts = bpy.props.IntProperty(name="Last Verts", default=0)
bpy.types.Mesh.mesh_cached_edges = bpy.props.IntProperty(name="Last Edges", default=0)
bpy.types.Mesh.static_usage = bpy.props.BoolProperty(name="Static Data Usage", default=True)
bpy.types.Curve.mesh_cached = bpy.props.BoolProperty(name="Mesh Cached", default=False)
bpy.types.Curve.static_usage = bpy.props.BoolProperty(name="Static Data Usage", default=True)
bpy.types.Mesh.mesh_cached = bpy.props.BoolProperty(name="Mesh Cached", description="No need to reexport mesh data", default=False)
bpy.types.Mesh.mesh_cached_verts = bpy.props.IntProperty(name="Last Verts", description="Number of vertices in last export", default=0)
bpy.types.Mesh.mesh_cached_edges = bpy.props.IntProperty(name="Last Edges", description="Number of edges in last export", default=0)
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 armature
bpy.types.Armature.armature_cached = bpy.props.BoolProperty(name="Armature Cached", default=False)
bpy.types.Armature.armature_cached = bpy.props.BoolProperty(name="Armature Cached", description="No need to reexport armature data", default=False)
# Actions
bpy.types.Armature.edit_actions = bpy.props.BoolProperty(name="Edit Actions", default=False)
bpy.types.Armature.edit_actions = bpy.props.BoolProperty(name="Edit Actions", description="Manually set used actions", default=False)
bpy.types.Armature.my_actiontraitlist = bpy.props.CollectionProperty(type=ListActionTraitItem)
bpy.types.Armature.actiontraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0)
bpy.types.Armature.actiontraitlist_index = bpy.props.IntProperty(name="Action index", default=0)
# For camera
bpy.types.Camera.frustum_culling = bpy.props.BoolProperty(name="Frustum Culling", default=True)
bpy.types.Camera.renderpath_path = bpy.props.StringProperty(name="Render Path", default="deferred_path")
bpy.types.Camera.renderpath_id = bpy.props.StringProperty(name="Render Path ID", default="deferred")
bpy.types.Camera.frustum_culling = bpy.props.BoolProperty(name="Frustum Culling", description="Perform frustum culling for this camera", default=True)
bpy.types.Camera.renderpath_path = bpy.props.StringProperty(name="Render Path", description="Render path nodes used for this camera", default="deferred_path")
bpy.types.Camera.renderpath_id = bpy.props.StringProperty(name="Render Path ID", description="Asset ID", default="deferred")
# TODO: Specify multiple material ids, merge ids from multiple cameras
bpy.types.Camera.renderpath_passes = bpy.props.StringProperty(name="Render Path Passes", default="")
bpy.types.Camera.renderpath_passes = bpy.props.StringProperty(name="Render Path Passes", description="Referenced render passes", default="")
bpy.types.Camera.mesh_context = bpy.props.StringProperty(name="Mesh", default="mesh")
bpy.types.Camera.mesh_context_empty = bpy.props.StringProperty(name="Mesh Empty", default="depthwrite")
bpy.types.Camera.shadows_context = bpy.props.StringProperty(name="Shadows", default="shadowmap")
bpy.types.Camera.translucent_context = bpy.props.StringProperty(name="Translucent", default="translucent")
bpy.types.Camera.overlay_context = bpy.props.StringProperty(name="Overlay", default="overlay")
bpy.types.Camera.is_probe = bpy.props.BoolProperty(name="Probe", default=False)
bpy.types.Camera.probe_generate_radiance = bpy.props.BoolProperty(name="Generate Radiance", default=False)
bpy.types.Camera.is_probe = bpy.props.BoolProperty(name="Probe", description="Render this camera as environment probe using Cycles", default=False)
bpy.types.Camera.probe_generate_radiance = bpy.props.BoolProperty(name="Generate Radiance", description="Generate radiance textures", default=False)
bpy.types.Camera.probe_texture = bpy.props.StringProperty(name="Texture", default="")
bpy.types.Camera.probe_num_mips = bpy.props.IntProperty(name="Number of mips", default=0)
bpy.types.Camera.probe_volume = bpy.props.StringProperty(name="Volume", default="")
bpy.types.Camera.probe_strength = bpy.props.FloatProperty(name="Strength", default=1.0)
bpy.types.Camera.probe_blending = bpy.props.FloatProperty(name="Blending", default=0.0)
bpy.types.Camera.is_mirror = bpy.props.BoolProperty(name="Mirror", default=False)
bpy.types.Camera.is_mirror = bpy.props.BoolProperty(name="Mirror", description="Render this camera into texture", default=False)
bpy.types.Camera.mirror_resolution_x = bpy.props.FloatProperty(name="X", default=512.0)
bpy.types.Camera.mirror_resolution_y = bpy.props.FloatProperty(name="Y", default=256.0)
bpy.types.Camera.last_decal_context = bpy.props.StringProperty(name="Decal Context", default='')
@ -308,7 +349,7 @@ def initProperties():
bpy.types.World.generate_fog_amounta = bpy.props.FloatProperty(name="Amount A", default=0.25, update=invalidate_shader_cache)
bpy.types.World.generate_fog_amountb = bpy.props.FloatProperty(name="Amount B", default=0.5, update=invalidate_shader_cache)
# Skin
bpy.types.World.generate_gpu_skin = bpy.props.BoolProperty(name="GPU Skinning", default=True, update=invalidate_shader_cache)
bpy.types.World.generate_gpu_skin = bpy.props.BoolProperty(name="GPU Skinning", description="Calculate skinning on GPU", default=True, update=invalidate_shader_cache)
bpy.types.World.generate_gpu_skin_max_bones = bpy.props.IntProperty(name="Max Bones", default=50, min=1, max=84, update=invalidate_shader_cache)
# Material override flags
bpy.types.World.force_no_culling = bpy.props.BoolProperty(name="Force No Culling", default=False)
@ -342,12 +383,12 @@ def initProperties():
('False', 'False', 'False')],
name = "Depth-Write", default='True')
# For scene
bpy.types.Scene.game_export = bpy.props.BoolProperty(name="Export", default=True)
bpy.types.Scene.game_export = bpy.props.BoolProperty(name="Export", description="Export scene data", default=True)
# For lamp
bpy.types.Lamp.lamp_clip_start = bpy.props.FloatProperty(name="Clip Start", default=0.1)
bpy.types.Lamp.lamp_clip_end = bpy.props.FloatProperty(name="Clip End", default=50.0)
bpy.types.Lamp.lamp_fov = bpy.props.FloatProperty(name="FoV", default=0.785)
bpy.types.Lamp.lamp_shadows_bias = bpy.props.FloatProperty(name="Shadows Bias", default=0.0001)
bpy.types.Lamp.lamp_fov = bpy.props.FloatProperty(name="Field of View", default=0.785)
bpy.types.Lamp.lamp_shadows_bias = bpy.props.FloatProperty(name="Shadows Bias", description="Depth offset for shadow acne", default=0.0001)
# Menu in object region
class ObjectPropsPanel(bpy.types.Panel):
@ -362,7 +403,9 @@ class ObjectPropsPanel(bpy.types.Panel):
if obj == None:
return
if bpy.data.worlds['Arm'].ArmExportHideRender == False:
wrd = bpy.data.worlds['Arm']
if wrd.ArmExportHideRender == False:
layout.prop(obj, 'hide_render')
hide = obj.hide_render
else:
@ -372,71 +415,73 @@ class ObjectPropsPanel(bpy.types.Panel):
if hide:
return
layout.prop(obj, 'spawn')
layout.prop(obj, 'game_visible')
layout.prop(obj, 'mobile')
if obj.type == 'MESH':
layout.prop(obj, 'instanced_children')
if obj.instanced_children:
layout.label('Location')
row = layout.row()
row.prop(obj, 'instanced_children_loc_x')
row.prop(obj, 'instanced_children_loc_y')
row.prop(obj, 'instanced_children_loc_z')
layout.label('Rotation')
row = layout.row()
row.prop(obj, 'instanced_children_rot_x')
row.prop(obj, 'instanced_children_rot_y')
row.prop(obj, 'instanced_children_rot_z')
layout.label('Scale')
row = layout.row()
row.prop(obj, 'instanced_children_scale_x')
row.prop(obj, 'instanced_children_scale_y')
row.prop(obj, 'instanced_children_scale_z')
layout.prop(obj, 'override_material')
if obj.override_material:
layout.prop(obj, 'override_material_name')
if obj.type == 'ARMATURE':
layout.prop(obj, 'bone_animation_enabled')
if obj.bone_animation_enabled:
layout.prop(obj, 'edit_actions_prop')
if obj.edit_actions_prop:
layout.prop_search(obj, "start_action_name_prop", obj.data, "my_actiontraitlist", "Start Action")
else:
layout.prop(obj, 'object_animation_enabled')
if (obj.type == 'ARMATURE' and obj.bone_animation_enabled) or (obj.type != 'ARMATURE' and obj.object_animation_enabled):
layout.prop(obj, 'edit_tracks_prop')
if obj.edit_tracks_prop:
layout.prop_search(obj, "start_track_name_prop", obj, "my_cliptraitlist", "Start Clip")
# Tracks list
layout.label("Clips")
animrow = layout.row()
animrows = 2
if len(obj.my_cliptraitlist) > 1:
animrows = 4
row = layout.row()
row.template_list("MY_UL_ClipTraitList", "The_List", obj, "my_cliptraitlist", obj, "cliptraitlist_index", rows=animrows)
col = row.column(align=True)
col.operator("my_cliptraitlist.new_item", icon='ZOOMIN', text="")
col.operator("my_cliptraitlist.delete_item", icon='ZOOMOUT', text="")
if len(obj.my_cliptraitlist) > 1:
col.separator()
col.operator("my_cliptraitlist.move_item", icon='TRIA_UP', text="").direction = 'UP'
col.operator("my_cliptraitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN'
if obj.cliptraitlist_index >= 0 and len(obj.my_cliptraitlist) > 0:
animitem = obj.my_cliptraitlist[obj.cliptraitlist_index]
layout.prop(wrd, 'ArmObjectAdvanced')
if wrd.ArmObjectAdvanced:
layout.prop(obj, 'spawn')
layout.prop(obj, 'game_visible')
layout.prop(obj, 'mobile')
if obj.type == 'MESH':
layout.prop(obj, 'instanced_children')
if obj.instanced_children:
layout.label('Location')
row = layout.row()
row.prop(animitem, "start_prop")
row.prop(animitem, "end_prop")
layout.prop(animitem, "speed_prop")
layout.prop(animitem, "loop_prop")
layout.prop(animitem, "reflect_prop")
row.prop(obj, 'instanced_children_loc_x')
row.prop(obj, 'instanced_children_loc_y')
row.prop(obj, 'instanced_children_loc_z')
layout.label('Rotation')
row = layout.row()
row.prop(obj, 'instanced_children_rot_x')
row.prop(obj, 'instanced_children_rot_y')
row.prop(obj, 'instanced_children_rot_z')
layout.label('Scale')
row = layout.row()
row.prop(obj, 'instanced_children_scale_x')
row.prop(obj, 'instanced_children_scale_y')
row.prop(obj, 'instanced_children_scale_z')
layout.prop(obj, 'override_material')
if obj.override_material:
layout.prop(obj, 'override_material_name')
if obj.type == 'ARMATURE':
layout.prop(obj, 'bone_animation_enabled')
if obj.bone_animation_enabled:
layout.prop(obj, 'edit_actions_prop')
if obj.edit_actions_prop:
layout.prop_search(obj, "start_action_name_prop", obj.data, "my_actiontraitlist", "Start Action")
else:
layout.prop(obj, 'object_animation_enabled')
if (obj.type == 'ARMATURE' and obj.bone_animation_enabled) or (obj.type != 'ARMATURE' and obj.object_animation_enabled):
layout.prop(obj, 'edit_tracks_prop')
if obj.edit_tracks_prop:
layout.prop_search(obj, "start_track_name_prop", obj, "my_cliptraitlist", "Start Clip")
# Tracks list
layout.label("Clips")
animrow = layout.row()
animrows = 2
if len(obj.my_cliptraitlist) > 1:
animrows = 4
row = layout.row()
row.template_list("MY_UL_ClipTraitList", "The_List", obj, "my_cliptraitlist", obj, "cliptraitlist_index", rows=animrows)
col = row.column(align=True)
col.operator("my_cliptraitlist.new_item", icon='ZOOMIN', text="")
col.operator("my_cliptraitlist.delete_item", icon='ZOOMOUT', text="")
if len(obj.my_cliptraitlist) > 1:
col.separator()
col.operator("my_cliptraitlist.move_item", icon='TRIA_UP', text="").direction = 'UP'
col.operator("my_cliptraitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN'
if obj.cliptraitlist_index >= 0 and len(obj.my_cliptraitlist) > 0:
animitem = obj.my_cliptraitlist[obj.cliptraitlist_index]
row = layout.row()
row.prop(animitem, "start_prop")
row.prop(animitem, "end_prop")
layout.prop(animitem, "speed_prop")
layout.prop(animitem, "loop_prop")
layout.prop(animitem, "reflect_prop")
# Menu in modifiers region
class ModifiersPropsPanel(bpy.types.Panel):
@ -486,13 +531,16 @@ class DataPropsPanel(bpy.types.Panel):
layout.prop_search(obj.data, "renderpath_path", bpy.data, "node_groups")
layout.operator("arm.reimport_paths_menu")
elif obj.type == 'MESH' or obj.type == 'FONT':
layout.prop(obj.data, 'static_usage')
layout.prop(obj.data, 'dynamic_usage')
layout.operator("arm.invalidate_cache")
elif obj.type == 'LAMP':
layout.prop(obj.data, 'lamp_clip_start')
layout.prop(obj.data, 'lamp_clip_end')
layout.prop(obj.data, 'lamp_fov')
layout.prop(obj.data, 'lamp_shadows_bias')
elif obj.type == 'SPEAKER':
layout.prop(obj.data, 'loop')
layout.prop(obj.data, 'stream')
elif obj.type == 'ARMATURE':
layout.prop(obj.data, 'edit_actions')
if obj.data.edit_actions:
@ -543,6 +591,7 @@ class ReimportPathsMenu(bpy.types.Menu):
layout.operator("arm.reimport_paths")
class ReimportPathsButton(bpy.types.Operator):
'''Reimport default render paths'''
bl_label = "Reimport Paths"
bl_idname = "arm.reimport_paths_menu"
@ -551,14 +600,16 @@ class ReimportPathsButton(bpy.types.Operator):
return {"FINISHED"}
class OBJECT_OT_REIMPORTPATHSButton(bpy.types.Operator):
'''Reimport default render paths'''
bl_idname = "arm.reimport_paths"
bl_label = "Reimport Paths"
def execute(self, context):
nodes_renderpath.load_library()
make_renderpath.load_library()
return{'FINISHED'}
class OBJECT_OT_INVALIDATECACHEButton(bpy.types.Operator):
'''Delete cached mesh data'''
bl_idname = "arm.invalidate_cache"
bl_label = "Invalidate Cache"
@ -700,11 +751,14 @@ class ArmoryPlayPanel(bpy.types.Panel):
if wrd.ArmPlayViewportCamera:
layout.prop(wrd, 'ArmPlayViewportNavigation')
layout.prop(wrd, 'ArmPlayConsole')
layout.prop(wrd, 'ArmPlayDeveloperTools')
layout.prop(wrd, 'ArmPlayLivePatch')
if wrd.ArmPlayLivePatch:
layout.prop(wrd, 'ArmPlayAutoBuild')
layout.prop(wrd, 'ArmPlayAdvanced')
if wrd.ArmPlayAdvanced:
layout.prop(wrd, 'ArmPlayConsole')
# layout.prop(wrd, 'ArmPlayDeveloperTools')
if utils.with_chromium():
layout.prop(wrd, 'ArmPlayLivePatch')
if wrd.ArmPlayLivePatch:
layout.prop(wrd, 'ArmPlayAutoBuild')
class ArmoryBuildPanel(bpy.types.Panel):
bl_label = "Armory Build"
@ -723,18 +777,21 @@ class ArmoryBuildPanel(bpy.types.Panel):
layout.operator("arm.kode_studio")
layout.operator("arm.clean")
layout.prop(wrd, 'ArmProjectTarget')
layout.prop(wrd, 'ArmCacheShaders')
layout.prop(wrd, 'ArmMinimize')
layout.prop(wrd, 'ArmOptimizeMesh')
layout.prop(wrd, 'ArmSampledAnimation')
layout.prop(wrd, 'ArmDeinterleavedBuffers')
layout.prop(wrd, 'generate_gpu_skin')
if wrd.generate_gpu_skin:
layout.prop(wrd, 'generate_gpu_skin_max_bones')
layout.prop(wrd, 'ArmProjectSamplesPerPixel')
layout.label('Libraries')
layout.prop(wrd, 'ArmPhysics')
layout.prop(wrd, 'ArmNavigation')
layout.prop(wrd, 'ArmBuildAdvanced')
if wrd.ArmBuildAdvanced:
layout.prop(wrd, 'ArmCacheShaders')
layout.prop(wrd, 'ArmCleanEnvmaps')
layout.prop(wrd, 'ArmMinimize')
layout.prop(wrd, 'ArmOptimizeMesh')
layout.prop(wrd, 'ArmSampledAnimation')
layout.prop(wrd, 'ArmDeinterleavedBuffers')
layout.prop(wrd, 'generate_gpu_skin')
if wrd.generate_gpu_skin:
layout.prop(wrd, 'generate_gpu_skin_max_bones')
layout.prop(wrd, 'ArmProjectSamplesPerPixel')
layout.label('Libraries')
layout.prop(wrd, 'ArmPhysics')
layout.prop(wrd, 'ArmNavigation')
class ArmoryProjectPanel(bpy.types.Panel):
bl_label = "Armory Project"
@ -743,6 +800,7 @@ class ArmoryProjectPanel(bpy.types.Panel):
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
info_text = ''
progress = 100.0
def draw(self, context):
layout = self.layout
@ -750,18 +808,21 @@ class ArmoryProjectPanel(bpy.types.Panel):
layout.prop(wrd, 'ArmProjectName')
layout.prop(wrd, 'ArmProjectPackage')
layout.prop_search(wrd, 'ArmKhafile', bpy.data, 'texts', 'Khafile')
layout.prop(wrd, 'ArmPlayActiveScene')
if wrd.ArmPlayActiveScene == False:
layout.prop_search(wrd, 'ArmProjectScene', bpy.data, 'scenes', 'Scene')
layout.prop(wrd, 'ArmExportHideRender')
layout.prop(wrd, 'ArmSpawnAllLayers')
layout.label('Publish Project')
layout.operator('arm.publish')
layout.prop(wrd, 'ArmPublishTarget')
layout.label('Armory v' + wrd.ArmVersion)
layout.operator('arm.check_updates')
layout.prop(wrd, 'ArmProjectAdvanced')
if wrd.ArmProjectAdvanced:
layout.prop(wrd, 'ArmPlayActiveScene')
if wrd.ArmPlayActiveScene == False:
layout.prop_search(wrd, 'ArmProjectScene', bpy.data, 'scenes', 'Scene')
layout.prop(wrd, 'ArmExportHideRender')
layout.prop(wrd, 'ArmSpawnAllLayers')
layout.label('Armory v' + wrd.ArmVersion)
layout.operator('arm.check_updates')
class ArmoryPlayButton(bpy.types.Operator):
'''Launch player in new window'''
bl_idname = 'arm.play'
bl_label = 'Play'
@ -772,6 +833,7 @@ class ArmoryPlayButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryPlayInViewportButton(bpy.types.Operator):
'''Launch player in 3D viewport'''
bl_idname = 'arm.play_in_viewport'
bl_label = 'Play in Viewport'
@ -792,6 +854,7 @@ class ArmoryPlayInViewportButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryStopButton(bpy.types.Operator):
'''Stop currently running player'''
bl_idname = 'arm.stop'
bl_label = 'Stop'
@ -800,17 +863,19 @@ class ArmoryStopButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryBuildButton(bpy.types.Operator):
'''Build and compile project'''
bl_idname = 'arm.build'
bl_label = 'Build'
def execute(self, context):
invalidate_shader_cache.enabled = False
make.build_project()
make.compile_project()
make.compile_project(watch=True)
invalidate_shader_cache.enabled = True
return{'FINISHED'}
class ArmoryPatchButton(bpy.types.Operator):
'''Update currently running player instance'''
bl_idname = 'arm.patch'
bl_label = 'Live Patch'
@ -822,6 +887,7 @@ class ArmoryPatchButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryFolderButton(bpy.types.Operator):
'''Open project folder'''
bl_idname = 'arm.folder'
bl_label = 'Project Folder'
@ -830,6 +896,7 @@ class ArmoryFolderButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryCheckUpdatesButton(bpy.types.Operator):
'''Open a website in the web-browser'''
bl_idname = 'arm.check_updates'
bl_label = 'Check for Updates'
@ -838,6 +905,7 @@ class ArmoryCheckUpdatesButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryKodeStudioButton(bpy.types.Operator):
'''Launch this project in Kode Studio'''
bl_idname = 'arm.kode_studio'
bl_label = 'Kode Studio'
bl_description = 'Open Project in Kode Studio'
@ -860,6 +928,7 @@ class ArmoryKodeStudioButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryCleanButton(bpy.types.Operator):
'''Delete all cached data'''
bl_idname = 'arm.clean'
bl_label = 'Clean'
@ -868,8 +937,9 @@ class ArmoryCleanButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryPublishButton(bpy.types.Operator):
'''Build project ready for publishing'''
bl_idname = 'arm.publish'
bl_label = 'Publish'
bl_label = 'Publish Project'
def execute(self, context):
make.publish_project()
@ -887,6 +957,9 @@ def draw_play_item(self, context):
# Info panel in header
def draw_info_item(self, context):
layout = self.layout
wrd = bpy.data.worlds['Arm']
if wrd.ArmProgress < 100:
layout.prop(wrd, 'ArmProgress')
if ArmoryProjectPanel.info_text != '':
layout.label(ArmoryProjectPanel.info_text)

View file

@ -2,7 +2,7 @@ import shutil
import bpy
import os
import json
from traits_params import *
from props_traits_params import *
from bpy.types import Menu, Panel, UIList
from bpy.props import *
import utils
@ -154,6 +154,7 @@ class LIST_OT_TraitMoveItem(bpy.types.Operator):
return{'FINISHED'}
class ArmoryEditScriptButton(bpy.types.Operator):
'''Edit script in Kode Studio'''
bl_idname = 'arm.edit_script'
bl_label = 'Edit Script'
@ -174,6 +175,7 @@ class ArmoryEditScriptButton(bpy.types.Operator):
return{'FINISHED'}
class ArmoryNewScriptDialog(bpy.types.Operator):
'''Create blank script'''
bl_idname = "arm.new_script"
bl_label = "New Script"
@ -193,6 +195,7 @@ class ArmoryNewScriptDialog(bpy.types.Operator):
return context.window_manager.invoke_props_dialog(self)
class ArmoryRefreshScriptsListButton(bpy.types.Operator):
'''Fetch all script names'''
bl_idname = 'arm.refresh_scripts_list'
bl_label = 'Refresh Scripts List'
@ -289,7 +292,6 @@ class ToolsTraitsPanel(bpy.types.Panel):
row = layout.row()
row.prop_search(item, "nodes_name_prop", bpy.data, "node_groups", "Tree")
# Registration
def register():
bpy.utils.register_module(__name__)
initObjectProperties()

View file

@ -7,7 +7,6 @@ import make
class SPACEARMORY_HT_header(Header):
bl_space_type = 'VIEW_GAME'
info_text = ''
is_paused = False
@ -28,17 +27,21 @@ class SPACEARMORY_HT_header(Header):
layout.label(SPACEARMORY_HT_header.info_text)
class ArmorySpaceStopButton(bpy.types.Operator):
'''Switch back to 3D view'''
bl_idname = 'arm.space_stop'
bl_label = 'Stop'
def execute(self, context):
area = bpy.context.area
if area == None:
area = make.play_project.play_area
area.type = 'VIEW_3D'
SPACEARMORY_HT_header.is_paused = False
SPACEARMORY_HT_header.info_text = ''
return{'FINISHED'}
class ArmorySpacePauseButton(bpy.types.Operator):
'''Pause rendering'''
bl_idname = 'arm.space_pause'
bl_label = 'Pause'
@ -47,6 +50,7 @@ class ArmorySpacePauseButton(bpy.types.Operator):
return{'FINISHED'}
class ArmorySpaceResumeButton(bpy.types.Operator):
'''Resume rendering'''
bl_idname = 'arm.space_resume'
bl_label = 'Resume'

View file

@ -1,12 +1,11 @@
import make
import nodes_logic
import nodes_renderpath
import nodes_world
import exporter
import traits_action
import traits_clip
import traits_params
import traits
import props_traits_action
import props_traits_clip
import props_traits_params
import props_traits
import props
import space_armory
import utils
@ -14,16 +13,15 @@ import lib.drop_to_ground
def register():
utils.register()
traits_action.register()
traits_clip.register()
props_traits_action.register()
props_traits_clip.register()
props.register()
make.register()
nodes_logic.register()
nodes_renderpath.register()
nodes_world.register()
exporter.register()
traits_params.register()
traits.register()
props_traits_params.register()
props_traits.register()
space_armory.register()
lib.drop_to_ground.register()
@ -32,12 +30,11 @@ def unregister():
make.unregister()
nodes_logic.unregister()
nodes_renderpath.unregister()
nodes_world.unregister()
exporter.unregister()
traits_params.unregister()
traits.unregister()
props_traits_params.unregister()
props_traits.unregister()
props.unregister()
traits_action.unregister()
traits_clip.unregister()
props_traits_action.unregister()
props_traits_clip.unregister()
space_armory.unregister()
lib.drop_to_ground.unregister()

View file

@ -37,8 +37,8 @@ project.addShaders('Sources/Shaders/**');
f.write(add_armory_library(sdk_path + '/lib/', 'haxebullet'))
# Electron live patching
if is_play and bpy.data.worlds['Arm'].ArmPlayLivePatch == True and bpy.data.worlds['Arm'].ArmPlayRuntime == 'Electron':
f.write("project.addDefine('WITH_PATCH_ELECTRON');\n")
# if is_play and bpy.data.worlds['Arm'].ArmPlayLivePatch == True and bpy.data.worlds['Arm'].ArmPlayRuntime == 'Electron':
# f.write("project.addDefine('WITH_PATCH_ELECTRON');\n")
# Native scripting
# f.write(add_armory_library(sdk_path + '/lib/', 'haxeduktape'))
@ -83,7 +83,7 @@ project.addShaders('Sources/Shaders/**');
f.write("\n\nresolve(project);\n")
# Write Main.hx
def write_main():
def write_main(is_play, in_viewport):
wrd = bpy.data.worlds['Arm']
resx, resy = utils.get_render_resolution()
#if not os.path.isfile('Sources/Main.hx'):
@ -99,9 +99,9 @@ class Main {
static inline var projectSamplesPerPixel = """ + str(wrd.ArmProjectSamplesPerPixel) + """;
static inline var projectScene = '""" + utils.get_project_scene_name() + """';
public static function main() {
iron.sys.CompileTime.importPackage('armory.trait');
iron.sys.CompileTime.importPackage('armory.renderpath');
iron.sys.CompileTime.importPackage('""" + wrd.ArmProjectPackage + """');
iron.system.CompileTime.importPackage('armory.trait');
iron.system.CompileTime.importPackage('armory.renderpath');
iron.system.CompileTime.importPackage('""" + wrd.ArmProjectPackage + """');
#if (js && WITH_PHYSICS)
untyped __js__("
function loadScript(url, callback) {
@ -127,7 +127,11 @@ class Main {
f.write("""
kha.System.init({title: projectName, width: projectWidth, height: projectHeight, samplesPerPixel: projectSamplesPerPixel}, function() {
iron.App.init(function() {
iron.Scene.setActive(projectScene, function(object:iron.object.Object) {
iron.Scene.setActive(projectScene, function(object:iron.object.Object) {""")
if utils.with_chromium() and in_viewport and is_play:
f.write("""
object.addTrait(new armory.trait.internal.EditorSpace());""")
f.write("""
});
});
});

View file

@ -49,7 +49,9 @@ uniform vec4 baseCol;
uniform float skinBones[skinMaxBones * 8]; // Dual quat
#endif
#ifdef _Probes
uniform mat4 W; // TODO: Conflicts with _HeightTex
#ifndef _HeightTex
uniform mat4 W;
#endif
#endif
#ifdef _Veloc
uniform mat4 prevWVP;
@ -182,14 +184,15 @@ void main() {
#endif
#ifdef _Billboard
mat4 constrWV = WV;
// Spherical
WV[0][0] = 1.0; WV[0][1] = 0.0; WV[0][2] = 0.0;
WV[1][0] = 0.0; WV[1][1] = 1.0; WV[1][2] = 0.0;
WV[2][0] = 0.0; WV[2][1] = 0.0; WV[2][2] = 1.0;
constrWV[0][0] = 1.0; constrWV[0][1] = 0.0; constrWV[0][2] = 0.0;
constrWV[1][0] = 0.0; constrWV[1][1] = 1.0; constrWV[1][2] = 0.0;
constrWV[2][0] = 0.0; constrWV[2][1] = 0.0; constrWV[2][2] = 1.0;
// Cylindrical
//WV[0][0] = 1.0; WV[0][1] = 0.0; WV[0][2] = 0.0;
//WV[2][0] = 0.0; WV[2][1] = 0.0; WV[2][2] = 1.0;
gl_Position = P * WV * sPos;
//constrWV[0][0] = 1.0; constrWV[0][1] = 0.0; constrWV[0][2] = 0.0;
//constrWV[2][0] = 0.0; constrWV[2][1] = 0.0; constrWV[2][2] = 1.0;
gl_Position = P * constrWV * sPos;
#else
gl_Position = WVP * sPos;
#endif

View file

@ -26,6 +26,10 @@ in vec3 nor;
#endif
uniform mat4 WVP;
#ifdef _Billboard
uniform mat4 WV;
uniform mat4 P;
#endif
#ifdef _Skinning
uniform float skinBones[skinMaxBones * 8];
#endif
@ -94,5 +98,17 @@ void main() {
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
#endif
#ifdef _Billboard
mat4 constrWV = WV;
// Spherical
constrWV[0][0] = 1.0; constrWV[0][1] = 0.0; constrWV[0][2] = 0.0;
constrWV[1][0] = 0.0; constrWV[1][1] = 1.0; constrWV[1][2] = 0.0;
constrWV[2][0] = 0.0; constrWV[2][1] = 0.0; constrWV[2][2] = 1.0;
// Cylindrical
//constrWV[0][0] = 1.0; constrWV[0][1] = 0.0; constrWV[0][2] = 0.0;
//constrWV[2][0] = 0.0; constrWV[2][1] = 0.0; constrWV[2][2] = 1.0;
gl_Position = P * constrWV * sPos;
#else
gl_Position = WVP * sPos;
#endif
}

View file

@ -26,12 +26,14 @@
"link": "_normalMatrix"
},
{
"name": "V",
"link": "_viewMatrix"
"name": "WV",
"link": "_worldViewMatrix",
"ifdef": ["_Billboard"]
},
{
"name": "P",
"link": "_projectionMatrix"
"link": "_projectionMatrix",
"ifdef": ["_Billboard"]
},
{
"name": "WVP",
@ -276,6 +278,16 @@
"name": "WVP",
"link": "_worldViewProjectionMatrix"
},
{
"name": "P",
"link": "_projectionMatrix",
"ifdef": ["_Billboard"]
},
{
"name": "WV",
"link": "_worldViewMatrix",
"ifdef": ["_Billboard"]
},
{
"name": "skinBones",
"link": "_skinBones",

View file

@ -27,8 +27,10 @@ in vec3 nor;
uniform mat4 W;
uniform mat4 N;
uniform mat4 V;
uniform mat4 P;
#ifdef _Billboard
uniform mat4 WV;
uniform mat4 P;
#endif
uniform mat4 WVP;
uniform mat4 LWVP;
uniform vec4 baseCol;
@ -120,20 +122,19 @@ void main() {
lPos = LWVP * sPos;
mat4 WV = V * W;
#ifdef _Billboard
mat4 constrWV = WV;
// Spherical
WV[0][0] = 1.0; WV[0][1] = 0.0; WV[0][2] = 0.0;
WV[1][0] = 0.0; WV[1][1] = 1.0; WV[1][2] = 0.0;
WV[2][0] = 0.0; WV[2][1] = 0.0; WV[2][2] = 1.0;
constrWV[0][0] = 1.0; constrWV[0][1] = 0.0; constrWV[0][2] = 0.0;
constrWV[1][0] = 0.0; constrWV[1][1] = 1.0; constrWV[1][2] = 0.0;
constrWV[2][0] = 0.0; constrWV[2][1] = 0.0; constrWV[2][2] = 1.0;
// Cylindrical
//WV[0][0] = 1.0; WV[0][1] = 0.0; WV[0][2] = 0.0;
//WV[2][0] = 0.0; WV[2][1] = 0.0; WV[2][2] = 1.0;
#endif
// gl_Position = P * WV * sPos;
//constrWV[0][0] = 1.0; constrWV[0][1] = 0.0; constrWV[0][2] = 0.0;
//constrWV[2][0] = 0.0; constrWV[2][1] = 0.0; constrWV[2][2] = 1.0;
gl_Position = P * constrWV * sPos;
#else
gl_Position = WVP * sPos;
#endif
#ifdef _Tex
texCoord = tex;