luboslenco 1 year ago
parent
commit
9b2e2a44a5
98 changed files with 2104 additions and 1554 deletions
  1. 20 16
      armorforge/Sources/tab_objects.ts
  2. 8 4
      armorlab/Sources/make_material.ts
  3. 1 1
      armorlab/Sources/nodes/brush_output_node.ts
  4. 1 1
      armorlab/Sources/nodes/image_texture_node.ts
  5. 9 9
      armorlab/Sources/nodes/inpaint_node.ts
  6. 10 10
      armorlab/Sources/nodes/photo_to_pbr_node.ts
  7. 1 1
      armorlab/Sources/nodes/rgb_node.ts
  8. 23 23
      armorlab/Sources/nodes/text_to_photo_node.ts
  9. 7 7
      armorlab/Sources/nodes/tiling_node.ts
  10. 4 4
      armorlab/Sources/nodes/upscale_node.ts
  11. 5 5
      armorlab/Sources/nodes/variance_node.ts
  12. 21 4
      armorlab/Sources/nodes_brush.ts
  13. 2 2
      armorlab/Sources/ui_nodes_ext.ts
  14. 6 3
      armorpaint/Sources/import_folder.ts
  15. 25 11
      armorpaint/Sources/make_material.ts
  16. 16 8
      armorpaint/Sources/make_mesh.ts
  17. 3 3
      armorpaint/Sources/make_paint.ts
  18. 1 1
      armorpaint/Sources/nodes/brush_output_node.ts
  19. 1 1
      armorpaint/Sources/nodes/input_node.ts
  20. 1 1
      armorpaint/Sources/nodes/tex_image_node.ts
  21. 20 2
      armorpaint/Sources/nodes_brush.ts
  22. 8 4
      armorpaint/Sources/render_path_paint.ts
  23. 15 10
      armorpaint/Sources/slot_brush.ts
  24. 15 11
      armorpaint/Sources/slot_font.ts
  25. 95 62
      armorpaint/Sources/slot_layer.ts
  26. 34 21
      armorpaint/Sources/slot_material.ts
  27. 35 17
      armorpaint/Sources/tab_layers.ts
  28. 7 7
      armorsculpt/Sources/export_obj.ts
  29. 9 7
      armorsculpt/Sources/import_mesh.ts
  30. 29 15
      armorsculpt/Sources/make_material.ts
  31. 18 14
      armorsculpt/Sources/make_mesh.ts
  32. 24 21
      armorsculpt/Sources/tab_layers.ts
  33. 2 1
      base/Sources/args.ts
  34. 52 25
      base/Sources/base.ts
  35. 6 3
      base/Sources/box_export.ts
  36. 23 18
      base/Sources/box_preferences.ts
  37. 5 2
      base/Sources/box_projects.ts
  38. 13 22
      base/Sources/config.ts
  39. 7 4
      base/Sources/context.ts
  40. 75 23
      base/Sources/export_arm.ts
  41. 2 1
      base/Sources/export_gpl.ts
  42. 2 1
      base/Sources/export_obj.ts
  43. 18 9
      base/Sources/export_texture.ts
  44. 3 6
      base/Sources/file.ts
  45. 4 2
      base/Sources/history.ts
  46. 47 24
      base/Sources/import_arm.ts
  47. 18 10
      base/Sources/import_blend_material.ts
  48. 17 16
      base/Sources/import_blend_mesh.ts
  49. 3 3
      base/Sources/import_envmap.ts
  50. 4 2
      base/Sources/import_font.ts
  51. 6 3
      base/Sources/import_mesh.ts
  52. 2 1
      base/Sources/import_texture.ts
  53. 8 4
      base/Sources/main.ts
  54. 110 64
      base/Sources/node_shader.ts
  55. 20 15
      base/Sources/node_shader_context.ts
  56. 2 2
      base/Sources/nodes/boolean_node.ts
  57. 5 1
      base/Sources/nodes/color_node.ts
  58. 2 2
      base/Sources/nodes/float_node.ts
  59. 2 2
      base/Sources/nodes/integer_node.ts
  60. 106 107
      base/Sources/nodes/math_node.ts
  61. 1 1
      base/Sources/nodes/null_node.ts
  62. 1 1
      base/Sources/nodes/random_node.ts
  63. 1 1
      base/Sources/nodes/separate_vector_node.ts
  64. 2 2
      base/Sources/nodes/string_node.ts
  65. 1 1
      base/Sources/nodes/time_node.ts
  66. 111 113
      base/Sources/nodes/vector_math_node.ts
  67. 5 5
      base/Sources/nodes/vector_node.ts
  68. 32 23
      base/Sources/nodes_material.ts
  69. 126 93
      base/Sources/parser_blend.ts
  70. 35 28
      base/Sources/parser_logic.ts
  71. 16 8
      base/Sources/parser_material.ts
  72. 6 3
      base/Sources/path.ts
  73. 99 80
      base/Sources/physics_body.ts
  74. 40 31
      base/Sources/physics_world.ts
  75. 16 23
      base/Sources/plugin.ts
  76. 27 15
      base/Sources/project.ts
  77. 3 1
      base/Sources/render_path_base.ts
  78. 2 1
      base/Sources/resource.ts
  79. 2 1
      base/Sources/tab_browser.ts
  80. 2 1
      base/Sources/tab_console.ts
  81. 8 4
      base/Sources/tab_materials.ts
  82. 2 1
      base/Sources/tab_meshes.ts
  83. 3 1
      base/Sources/tab_plugins.ts
  84. 3 7
      base/Sources/tab_script.ts
  85. 6 3
      base/Sources/tab_textures.ts
  86. 17 10
      base/Sources/translator.ts
  87. 12 7
      base/Sources/ui_base.ts
  88. 4 2
      base/Sources/ui_files.ts
  89. 30 8
      base/Sources/ui_header.ts
  90. 29 16
      base/Sources/ui_menu.ts
  91. 62 31
      base/Sources/ui_nodes.ts
  92. 2 1
      base/Sources/ui_status.ts
  93. 15 5
      base/Sources/ui_toolbar.ts
  94. 325 337
      base/Sources/uniforms_ext.ts
  95. 10 5
      base/Sources/util_mesh.ts
  96. 4 2
      base/Sources/util_particle.ts
  97. 2 1
      base/Sources/util_render.ts
  98. 4 2
      base/Sources/viewport.ts

+ 20 - 16
armorforge/Sources/tab_objects.ts

@@ -73,7 +73,8 @@ function tab_objects_draw(htab: zui_handle_t) {
 						if (ui_menu_button(ui, "Assign Material")) {
 							tab_objects_material_id++;
 
-							for (let sh of _scene_raw.shader_datas) {
+							for (let i: i32 = 0; i < _scene_raw.shader_datas.length; ++i) {
+								let sh: shader_data_t = _scene_raw.shader_datas[i];
 								if (sh.name == "Material_data") {
 									let s: shader_data_t = json_parse(json_stringify(sh));
 									s.name = "TempMaterial_data" + tab_objects_material_id;
@@ -82,7 +83,8 @@ function tab_objects_draw(htab: zui_handle_t) {
 								}
 							}
 
-							for (let mat of _scene_raw.material_datas) {
+							for (let i: i32 = 0; i < _scene_raw.material_datas.length; ++i) {
+								let mat: material_data_t = _scene_raw.material_datas[i];
 								if (mat.name == "Material") {
 									let m: material_data_t = json_parse(json_stringify(mat));
 									m.name = "TempMaterial" + tab_objects_material_id;
@@ -102,7 +104,8 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				if (b) {
 					let current_y = ui._y;
-					for (let child of current_object.children) {
+					for (let i: i32 = 0; i < current_object.children.length; ++i) {
+						let child: object_t = current_object.children[i];
 						// ui.indent();
 						draw_list(list_handle, child);
 						// ui.unindent();
@@ -114,7 +117,8 @@ function tab_objects_draw(htab: zui_handle_t) {
 					g2_set_color(0xffffffff);
 				}
 			}
-			for (let c of _scene_root.children) {
+			for (let i: i32 = 0; i < _scene_root.children.length) {
+				let c: object_t = _scene_root.children[i];
 				draw_list(zui_handle("tabobjects_1"), c);
 			}
 
@@ -142,21 +146,21 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				h = zui_handle("tabobjects_4");
 				h.text = roundfp(local_pos.x) + "";
-				f = parseFloat(zui_text_input(h, "X"));
+				f = parse_float(zui_text_input(h, "X"));
 				if (h.changed) {
 					local_pos.x = f;
 				}
 
 				h = zui_handle("tabobjects_5");
 				h.text = roundfp(local_pos.y) + "";
-				f = parseFloat(zui_text_input(h, "Y"));
+				f = parse_float(zui_text_input(h, "Y"));
 				if (h.changed) {
 					local_pos.y = f;
 				}
 
 				h = zui_handle("tabobjects_6");
 				h.text = roundfp(local_pos.z) + "";
-				f = parseFloat(zui_text_input(h, "Z"));
+				f = parse_float(zui_text_input(h, "Z"));
 				if (h.changed) {
 					local_pos.z = f;
 				}
@@ -166,7 +170,7 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				h = zui_handle("tabobjects_7");
 				h.text = roundfp(rot.x) + "";
-				f = parseFloat(zui_text_input(h, "X"));
+				f = parse_float(zui_text_input(h, "X"));
 				let changed = false;
 				if (h.changed) {
 					changed = true;
@@ -175,7 +179,7 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				h = zui_handle("tabobjects_8");
 				h.text = roundfp(rot.y) + "";
-				f = parseFloat(zui_text_input(h, "Y"));
+				f = parse_float(zui_text_input(h, "Y"));
 				if (h.changed) {
 					changed = true;
 					rot.y = f;
@@ -183,7 +187,7 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				h = zui_handle("tabobjects_9");
 				h.text = roundfp(rot.z) + "";
-				f = parseFloat(zui_text_input(h, "Z"));
+				f = parse_float(zui_text_input(h, "Z"));
 				if (h.changed) {
 					changed = true;
 					rot.z = f;
@@ -203,21 +207,21 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				h = zui_handle("tabobjects_10");
 				h.text = roundfp(scale.x) + "";
-				f = parseFloat(zui_text_input(h, "X"));
+				f = parse_float(zui_text_input(h, "X"));
 				if (h.changed) {
 					scale.x = f;
 				}
 
 				h = zui_handle("tabobjects_11");
 				h.text = roundfp(scale.y) + "";
-				f = parseFloat(zui_text_input(h, "Y"));
+				f = parse_float(zui_text_input(h, "Y"));
 				if (h.changed) {
 					scale.y = f;
 				}
 
 				h = zui_handle("tabobjects_12");
 				h.text = roundfp(scale.z) + "";
-				f = parseFloat(zui_text_input(h, "Z"));
+				f = parse_float(zui_text_input(h, "Z"));
 				if (h.changed) {
 					scale.z = f;
 				}
@@ -227,21 +231,21 @@ function tab_objects_draw(htab: zui_handle_t) {
 
 				h = zui_handle("tabobjects_13");
 				h.text = roundfp(dim.x) + "";
-				f = parseFloat(zui_text_input(h, "X"));
+				f = parse_float(zui_text_input(h, "X"));
 				if (h.changed) {
 					dim.x = f;
 				}
 
 				h = zui_handle("tabobjects_14");
 				h.text = roundfp(dim.y) + "";
-				f = parseFloat(zui_text_input(h, "Y"));
+				f = parse_float(zui_text_input(h, "Y"));
 				if (h.changed) {
 					dim.y = f;
 				}
 
 				h = zui_handle("tabobjects_15");
 				h.text = roundfp(dim.z) + "";
-				f = parseFloat(zui_text_input(h, "Z"));
+				f = parse_float(zui_text_input(h, "Z"));
 				if (h.changed) {
 					dim.z = f;
 				}

+ 8 - 4
armorlab/Sources/make_material.ts

@@ -6,7 +6,8 @@ let make_material_height_used = false;
 function make_material_parse_mesh_material() {
 	let m = project_material_data;
 
-	for (let c of m._.shader.contexts) {
+	for (let i: i32 = 0; i < m._.shader.contexts.length; ++i) {
+		let c = m._.shader.contexts[i];
 		if (c.name == "mesh") {
 			array_remove(m._.shader.contexts, c);
 			array_remove(m._.shader._.contexts, c);
@@ -45,7 +46,8 @@ function make_material_make_voxel(m: material_data_t) {
 	let rebuild = true; // heightUsed;
 	if (config_raw.rp_gi != false && rebuild) {
 		let scon: shader_context_t = null;
-		for (let c of m._.shader._.contexts) {
+		for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+			let c = m._.shader._.contexts[i];
 			if (c.name == "voxel") {
 				scon = c;
 				break;
@@ -60,7 +62,8 @@ function make_material_parse_paint_material() {
 	let m = project_material_data;
 	let scon: shader_context_t = null;
 	let mcon: material_context_t = null;
-	for (let c of m._.shader.contexts) {
+	for (let i: i32 = 0; i < m._.shader.contexts.length; ++i) {
+		let c = m._.shader.contexts[i];
 		if (c.name == "paint") {
 			array_remove(m._.shader.contexts, c);
 			array_remove(m._.shader._.contexts, c);
@@ -68,7 +71,8 @@ function make_material_parse_paint_material() {
 			break;
 		}
 	}
-	for (let c of m.contexts) {
+	for (let i: i32 = 0; i < m.contexts.length) {
+		let c = m.contexts[i];
 		if (c.name == "paint") {
 			array_remove(m.contexts, c);
 			array_remove(m._.contexts, c);

+ 1 - 1
armorlab/Sources/nodes/brush_output_node.ts

@@ -11,7 +11,7 @@ type brush_output_node_t = {
 
 let brush_output_node_inst: brush_output_node_t = null;
 
-function brush_output_node_create(): brush_output_node_t {
+function brush_output_node_create(arg: any): brush_output_node_t {
 	let n: brush_output_node_t = {};
 	n.base = logic_node_create();
 

+ 1 - 1
armorlab/Sources/nodes/image_texture_node.ts

@@ -5,7 +5,7 @@ type image_texture_node_t = {
 	color_space?: string;
 };
 
-function image_texture_node_create(): image_texture_node_t {
+function image_texture_node_create(arg: any): image_texture_node_t {
 	let n: image_texture_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = image_texture_node_get_as_image;

+ 9 - 9
armorlab/Sources/nodes/inpaint_node.ts

@@ -14,7 +14,7 @@ let inpaint_node_prompt = "";
 let inpaint_node_strength = 0.5;
 let inpaint_node_auto = true;
 
-function inpaint_node_create(): inpaint_node_t {
+function inpaint_node_create(arg: any): inpaint_node_t {
 	let n: inpaint_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = inpaint_node_get_as_image;
@@ -121,13 +121,13 @@ function inpaint_node_sd_inpaint(image: image_t, mask: image_t, done: (img: imag
 	let f32mask = f32_array_create(4 * 64 * 64);
 
 	let vae_encoder_blob: buffer_t = data_get_blob("models/sd_vae_encoder.quant.onnx");
-	// for (let x = 0; x < math_floor(image.width / 512); ++x) {
-		// for (let y = 0; y < math_floor(image.height / 512); ++y) {
+	// for (let x: i32 = 0; x < math_floor(image.width / 512); ++x) {
+		// for (let y: i32 = 0; y < math_floor(image.height / 512); ++y) {
 			let x = 0;
 			let y = 0;
 
-			for (let xx = 0; xx < 64; ++xx) {
-				for (let yy = 0; yy < 64; ++yy) {
+			for (let xx: i32 = 0; xx < 64; ++xx) {
+				for (let yy: i32 = 0; yy < 64; ++yy) {
 					// let step = math_floor(512 / 64);
 					// let j = (yy * step * mask.width + xx * step) + (y * 512 * mask.width + x * 512);
 					let step = math_floor(mask.width / 64);
@@ -149,7 +149,7 @@ function inpaint_node_sd_inpaint(image: image_t, mask: image_t, done: (img: imag
 			bytes_img = image_get_pixels(inpaint_node_temp);
 			let u8a = new u8_array_t(bytes_img);
 			let f32a = f32_array_create(3 * 512 * 512);
-			for (let i = 0; i < (512 * 512); ++i) {
+			for (let i: i32 = 0; i < (512 * 512); ++i) {
 				f32a[i                ] = (u8a[i * 4    ] / 255.0) * 2.0 - 1.0;
 				f32a[i + 512 * 512    ] = (u8a[i * 4 + 1] / 255.0) * 2.0 - 1.0;
 				f32a[i + 512 * 512 * 2] = (u8a[i * 4 + 2] / 255.0) * 2.0 - 1.0;
@@ -157,13 +157,13 @@ function inpaint_node_sd_inpaint(image: image_t, mask: image_t, done: (img: imag
 
 			let latents_buf = krom_ml_inference(vae_encoder_blob, [f32a.buffer], [[1, 3, 512, 512]], [1, 4, 64, 64], config_raw.gpu_inference);
 			let latents = new f32_array_t(latents_buf);
-			for (let i = 0; i < latents.length; ++i) {
+			for (let i: i32 = 0; i < latents.length; ++i) {
 				latents[i] = 0.18215 * latents[i];
 			}
 			let latents_orig = array_slice(latents, 0, latents.length);
 
 			let noise = f32_array_create(latents.length);
-			for (let i = 0; i < noise.length; ++i) noise[i] = math_cos(2.0 * 3.14 * random_node_get_float()) * math_sqrt(-2.0 * math_log(random_node_get_float()));
+			for (let i: i32 = 0; i < noise.length; ++i) noise[i] = math_cos(2.0 * 3.14 * random_node_get_float()) * math_sqrt(-2.0 * math_log(random_node_get_float()));
 
 			let num_inference_steps = 50;
 			let init_timestep = math_floor(num_inference_steps * inpaint_node_strength);
@@ -171,7 +171,7 @@ function inpaint_node_sd_inpaint(image: image_t, mask: image_t, done: (img: imag
 			let alphas_cumprod = TextToPhotoNode.alphas_cumprod;
 			let sqrt_alpha_prod = math_pow(alphas_cumprod[timestep], 0.5);
 			let sqrt_one_minus_alpha_prod = math_pow(1.0 - alphas_cumprod[timestep], 0.5);
-			for (let i = 0; i < latents.length; ++i) {
+			for (let i: i32 = 0; i < latents.length; ++i) {
 				latents[i] = sqrt_alpha_prod * latents[i] + sqrt_one_minus_alpha_prod * noise[i];
 			}
 

+ 10 - 10
armorlab/Sources/nodes/photo_to_pbr_node.ts

@@ -12,7 +12,7 @@ let photo_to_pbr_node_border_w: i32 = 64;
 let photo_to_pbr_node_tile_w: i32 = 2048;
 let photo_to_pbr_node_tile_with_border_w: i32 = photo_to_pbr_node_tile_w + photo_to_pbr_node_border_w * 2;
 
-function photo_to_pbr_node_create(): photo_to_pbr_node_t {
+function photo_to_pbr_node_create(arg: any): photo_to_pbr_node_t {
 	let n: photo_to_pbr_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = photo_to_pbr_node_get_as_image;
@@ -29,7 +29,7 @@ function photo_to_pbr_node_create(): photo_to_pbr_node_t {
 function photo_to_pbr_node_init() {
 	if (photo_to_pbr_node_images == null) {
 		photo_to_pbr_node_images = [];
-		for (let i = 0; i < photo_to_pbr_node_model_names.length; ++i) {
+		for (let i: i32 = 0; i < photo_to_pbr_node_model_names.length; ++i) {
 			array_push(photo_to_pbr_node_images, image_create_render_target(config_get_texture_res_x(), config_get_texture_res_y()));
 		}
 	}
@@ -54,7 +54,7 @@ function photo_to_pbr_node_get_as_image(self: photo_to_pbr_node_t, from: i32, do
 			let tiles_x = math_floor(config_get_texture_res_x() / photo_to_pbr_node_tile_w);
 			let tiles_y = math_floor(config_get_texture_res_y() / photo_to_pbr_node_tile_w);
 			let num_tiles = tiles_x * tiles_y;
-			for (let i = 0; i < num_tiles; ++i) {
+			for (let i: i32 = 0; i < num_tiles; ++i) {
 				let x = i % tiles_x;
 				let y = math_floor(i / tiles_x);
 
@@ -71,7 +71,7 @@ function photo_to_pbr_node_get_as_image(self: photo_to_pbr_node_t, from: i32, do
 				let bytes_img = image_get_pixels(photo_to_pbr_node_temp);
 				let u8a = new u8_array_t(bytes_img);
 				let f32a = f32_array_create(3 * photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w);
-				for (let i = 0; i < (photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w); ++i) {
+				for (let i: i32 = 0; i < (photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w); ++i) {
 					f32a[i                                        ] = (u8a[i * 4    ] / 255 - 0.5) / 0.5;
 					f32a[i + photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w    ] = (u8a[i * 4 + 1] / 255 - 0.5) / 0.5;
 					f32a[i + photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w * 2] = (u8a[i * 4 + 2] / 255 - 0.5) / 0.5;
@@ -83,7 +83,7 @@ function photo_to_pbr_node_get_as_image(self: photo_to_pbr_node_t, from: i32, do
 				u8a = u8_array_create(4 * photo_to_pbr_node_tile_w * photo_to_pbr_node_tile_w);
 				let offset_g = (from == channel_type_t.BASE_COLOR || from == channel_type_t.NORMAL_MAP) ? photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w : 0;
 				let offset_b = (from == channel_type_t.BASE_COLOR || from == channel_type_t.NORMAL_MAP) ? photo_to_pbr_node_tile_with_border_w * photo_to_pbr_node_tile_with_border_w * 2 : 0;
-				for (let i = 0; i < (photo_to_pbr_node_tile_w * photo_to_pbr_node_tile_w); ++i) {
+				for (let i: i32 = 0; i < (photo_to_pbr_node_tile_w * photo_to_pbr_node_tile_w); ++i) {
 					let x = photo_to_pbr_node_border_w + i % photo_to_pbr_node_tile_w;
 					let y = photo_to_pbr_node_border_w + math_floor(i / photo_to_pbr_node_tile_w);
 					u8a[i * 4    ] = math_floor((ar[y * photo_to_pbr_node_tile_with_border_w + x          ] * 0.5 + 0.5) * 255);
@@ -97,8 +97,8 @@ function photo_to_pbr_node_get_as_image(self: photo_to_pbr_node_t, from: i32, do
 				if (i > 0) {
 					if (x > 0) {
 						let ar = tile_floats[i - 1];
-						for (let yy = 0; yy < photo_to_pbr_node_tile_w; ++yy) {
-							for (let xx = 0; xx < photo_to_pbr_node_border_w; ++xx) {
+						for (let yy: i32 = 0; yy < photo_to_pbr_node_tile_w; ++yy) {
+							for (let xx: i32 = 0; xx < photo_to_pbr_node_border_w; ++xx) {
 								let i = yy * photo_to_pbr_node_tile_w + xx;
 								let a = u8a[i * 4];
 								let b = u8a[i * 4 + 1];
@@ -122,8 +122,8 @@ function photo_to_pbr_node_get_as_image(self: photo_to_pbr_node_t, from: i32, do
 					}
 					if (y > 0) {
 						let ar = tile_floats[i - tiles_x];
-						for (let xx = 0; xx < photo_to_pbr_node_tile_w; ++xx) {
-							for (let yy = 0; yy < photo_to_pbr_node_border_w; ++yy) {
+						for (let xx: i32 = 0; xx < photo_to_pbr_node_tile_w; ++xx) {
+							for (let yy: i32 = 0; yy < photo_to_pbr_node_border_w; ++yy) {
 								let i = yy * photo_to_pbr_node_tile_w + xx;
 								let a = u8a[i * 4];
 								let b = u8a[i * 4 + 1];
@@ -170,7 +170,7 @@ function photo_to_pbr_node_get_as_image(self: photo_to_pbr_node_t, from: i32, do
 ///if (krom_metal || krom_vulkan)
 function photo_to_pbr_node_bgra_swap(buffer: buffer_t) {
 	let u8a = new u8_array_t(buffer);
-	for (let i = 0; i < math_floor(buffer_size(buffer) / 4); ++i) {
+	for (let i: i32 = 0; i < math_floor(buffer_size(buffer) / 4); ++i) {
 		let r = u8a[i * 4];
 		u8a[i * 4] = u8a[i * 4 + 2];
 		u8a[i * 4 + 2] = r;

+ 1 - 1
armorlab/Sources/nodes/rgb_node.ts

@@ -4,7 +4,7 @@ type rgb_node_t = {
 	image?: image_t;
 };
 
-function rgb_node_create(): rgb_node_t {
+function rgb_node_create(arg: any): rgb_node_t {
 	let n: rgb_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = rgb_node_get_as_image;

+ 23 - 23
armorlab/Sources/nodes/text_to_photo_node.ts

@@ -10,7 +10,7 @@ let text_to_photo_node_text_encoder_blob : buffer_t;
 let text_to_photo_node_unet_blob : buffer_t;
 let text_to_photo_node_vae_decoder_blob : buffer_t;
 
-function text_to_photo_node_create(): text_to_photo_node_t {
+function text_to_photo_node_create(arg: any): text_to_photo_node_t {
 	let n: text_to_photo_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = text_to_photo_node_get_as_image;
@@ -53,10 +53,10 @@ function text_to_photo_node_text_encoder(prompt: string, inpaint_latents: f32_ar
 	console_progress(tr("Processing") + " - " + tr("Text to Photo"));
 	base_notify_on_next_frame(function () {
 		let words = string_split(string_replace_all(string_replace_all(string_replace_all(prompt, "\n", " "), ",", " , "), "  ", " ").trim(), " ");
-		for (let i = 0; i < words.length; ++i) {
+		for (let i: i32 = 0; i < words.length; ++i) {
 			text_to_photo_node_text_input_ids[i + 1] = to_lower_case(text_to_photo_node_vocab[words[i]) + "</w>"];
 		}
-		for (let i = words.length; i < (text_to_photo_node_text_input_ids.length - 1); ++i) {
+		for (let i: i32 = words.length; i < (text_to_photo_node_text_input_ids.length - 1); ++i) {
 			text_to_photo_node_text_input_ids[i + 1] = 49407; // <|endoftext|>
 		}
 
@@ -69,18 +69,18 @@ function text_to_photo_node_text_encoder(prompt: string, inpaint_latents: f32_ar
 		let uncond_embeddings = new f32_array_t(uncond_embeddings_buf);
 
 		let f32a = f32_array_create(uncond_embeddings.length + text_embeddings.length);
-		for (let i = 0; i < uncond_embeddings.length; ++i) f32a[i] = uncond_embeddings[i];
-		for (let i = 0; i < text_embeddings.length; ++i) f32a[i + uncond_embeddings.length] = text_embeddings[i];
+		for (let i: i32 = 0; i < uncond_embeddings.length; ++i) f32a[i] = uncond_embeddings[i];
+		for (let i: i32 = 0; i < text_embeddings.length; ++i) f32a[i + uncond_embeddings.length] = text_embeddings[i];
 		text_embeddings = f32a;
 
 		let width = 512;
 		let height = 512;
 		let latents = f32_array_create(1 * 4 * math_floor(height / 8) * math_floor(width / 8));
 		if (inpaint_latents == null) {
-			for (let i = 0; i < latents.length; ++i) latents[i] = math_cos(2.0 * 3.14 * random_node_get_float()) * math_sqrt(-2.0 * math_log(random_node_get_float()));
+			for (let i: i32 = 0; i < latents.length; ++i) latents[i] = math_cos(2.0 * 3.14 * random_node_get_float()) * math_sqrt(-2.0 * math_log(random_node_get_float()));
 		}
 		else {
-			for (let i = 0; i < latents.length; ++i) latents[i] = inpaint_latents[i];
+			for (let i: i32 = 0; i < latents.length; ++i) latents[i] = inpaint_latents[i];
 		}
 
 		done(latents, text_embeddings);
@@ -102,20 +102,20 @@ function text_to_photo_node_unet(latents: f32_array_t, text_embeddings: f32_arra
 		console_progress(tr("Processing") + " - " + tr("Text to Photo") + " (" + (counter + 1) + "/" + (50 - offset) + ")");
 
 		let timestep = text_to_photo_node_timesteps[counter + offset];
-		for (let i = 0; i < latents.length; ++i) latent_model_input[i] = latents[i];
-		for (let i = 0; i < latents.length; ++i) latent_model_input[i + latents.length] = latents[i];
+		for (let i: i32 = 0; i < latents.length; ++i) latent_model_input[i] = latents[i];
+		for (let i: i32 = 0; i < latents.length; ++i) latent_model_input[i + latents.length] = latents[i];
 
 		let t32 = i32_array_create(2);
 		t32[0] = timestep;
 		let noise_pred_buf = krom_ml_inference(text_to_photo_node_unet_blob, [latent_model_input.buffer, t32.buffer, text_embeddings.buffer], [[2, 4, 64, 64], [1], [2, 77, 768]], [2, 4, 64, 64], config_raw.gpu_inference);
 		let noise_pred = new f32_array_t(noise_pred_buf);
 
-		for (let i = 0; i < noise_pred_uncond.length; ++i) noise_pred_uncond[i] = noise_pred[i];
-		for (let i = 0; i < noise_pred_text.length; ++i) noise_pred_text[i] = noise_pred[noise_pred_uncond.length + i];
+		for (let i: i32 = 0; i < noise_pred_uncond.length; ++i) noise_pred_uncond[i] = noise_pred[i];
+		for (let i: i32 = 0; i < noise_pred_text.length; ++i) noise_pred_text[i] = noise_pred[noise_pred_uncond.length + i];
 
 		let guidance_scale = 7.5;
 		noise_pred = f32_array_create(noise_pred_uncond.length);
-		for (let i = 0; i < noise_pred_uncond.length; ++i) {
+		for (let i: i32 = 0; i < noise_pred_uncond.length; ++i) {
 			noise_pred[i] = noise_pred_uncond[i] + guidance_scale * (noise_pred_text[i] - noise_pred_uncond[i]);
 		}
 
@@ -134,7 +134,7 @@ function text_to_photo_node_unet(latents: f32_array_t, text_embeddings: f32_arra
 		}
 		else if (ets.length == 1 && counter == 1) {
 			let _noise_pred = f32_array_create(noise_pred.length);
-			for (let i = 0; i < noise_pred.length; ++i) {
+			for (let i: i32 = 0; i < noise_pred.length; ++i) {
 				_noise_pred[i] = (noise_pred[i] + ets[ets.length - 1][i]) / 2;
 			}
 			noise_pred = _noise_pred;
@@ -143,21 +143,21 @@ function text_to_photo_node_unet(latents: f32_array_t, text_embeddings: f32_arra
 		}
 		else if (ets.length == 2) {
 			let _noise_pred = f32_array_create(noise_pred.length);
-			for (let i = 0; i < noise_pred.length; ++i) {
+			for (let i: i32 = 0; i < noise_pred.length; ++i) {
 				_noise_pred[i] = (3 * ets[ets.length - 1][i] - ets[ets.length - 2][i]) / 2;
 			}
 			noise_pred = _noise_pred;
 		}
 		else if (ets.length == 3) {
 			let _noise_pred = f32_array_create(noise_pred.length);
-			for (let i = 0; i < noise_pred.length; ++i) {
+			for (let i: i32 = 0; i < noise_pred.length; ++i) {
 				_noise_pred[i] = (23 * ets[ets.length - 1][i] - 16 * ets[ets.length - 2][i] + 5 * ets[ets.length - 3][i]) / 12;
 			}
 			noise_pred = _noise_pred;
 		}
 		else {
 			let _noise_pred = f32_array_create(noise_pred.length);
-			for (let i = 0; i < noise_pred.length; ++i) {
+			for (let i: i32 = 0; i < noise_pred.length; ++i) {
 				_noise_pred[i] = (1 / 24) * (55 * ets[ets.length - 1][i] - 59 * ets[ets.length - 2][i] + 37 * ets[ets.length - 3][i] - 9 * ets[ets.length - 4][i]);
 			}
 			noise_pred = _noise_pred;
@@ -169,25 +169,25 @@ function text_to_photo_node_unet(latents: f32_array_t, text_embeddings: f32_arra
 		let beta_prod_t_prev = 1 - alpha_prod_t_prev;
 		let latents_coeff = math_pow(alpha_prod_t_prev / alpha_prod_t, (0.5));
 		let noise_pred_denom_coeff = alpha_prod_t * math_pow(beta_prod_t_prev, (0.5)) + math_pow(alpha_prod_t * beta_prod_t * alpha_prod_t_prev, (0.5));
-		for (let i = 0; i < latents.length; ++i) {
+		for (let i: i32 = 0; i < latents.length; ++i) {
 			latents[i] = (latents_coeff * latents[i] - (alpha_prod_t_prev - alpha_prod_t) * noise_pred[i] / noise_pred_denom_coeff);
 		}
 		counter += 1;
 
 		if (mask != null) {
 			let noise = f32_array_create(latents.length);
-			for (let i = 0; i < noise.length; ++i) {
+			for (let i: i32 = 0; i < noise.length; ++i) {
 				noise[i] = math_cos(2.0 * 3.14 * random_node_get_float()) * math_sqrt(-2.0 * math_log(random_node_get_float()));
 			}
 			let sqrt_alpha_prod = math_pow(text_to_photo_node_alphas_cumprod[timestep], 0.5);
 			let sqrt_one_minus_alpha_prod = math_pow(1.0 - text_to_photo_node_alphas_cumprod[timestep], 0.5);
 
 			let init_latents_proper = f32_array_create(latents.length);
-			for (let i = 0; i < init_latents_proper.length; ++i) {
+			for (let i: i32 = 0; i < init_latents_proper.length; ++i) {
 				init_latents_proper[i] = sqrt_alpha_prod * latents_orig[i] + sqrt_one_minus_alpha_prod * noise[i];
 			}
 
-			for (let i = 0; i < latents.length; ++i) {
+			for (let i: i32 = 0; i < latents.length; ++i) {
 				latents[i] = (init_latents_proper[i] * mask[i]) + (latents[i] * (1.0 - mask[i]));
 			}
 		}
@@ -203,21 +203,21 @@ function text_to_photo_node_unet(latents: f32_array_t, text_embeddings: f32_arra
 function text_to_photo_node_vae_decoder(latents: f32_array_t, upscale: bool, done: (img: image_t)=>void) {
 	console_progress(tr("Processing") + " - " + tr("Text to Photo"));
 	base_notify_on_next_frame(function () {
-		for (let i = 0; i < latents.length; ++i) {
+		for (let i: i32 = 0; i < latents.length; ++i) {
 			latents[i] = 1.0 / 0.18215 * latents[i];
 		}
 
 		let pyimage_buf = krom_ml_inference(text_to_photo_node_vae_decoder_blob, [latents.buffer], [[1, 4, 64, 64]], [1, 3, 512, 512], config_raw.gpu_inference);
 		let pyimage = new f32_array_t(pyimage_buf);
 
-		for (let i = 0; i < pyimage.length; ++i) {
+		for (let i: i32 = 0; i < pyimage.length; ++i) {
 			pyimage[i] = pyimage[i] / 2.0 + 0.5;
 			if (pyimage[i] < 0) pyimage[i] = 0;
 			else if (pyimage[i] > 1) pyimage[i] = 1;
 		}
 
 		let u8a = u8_array_create(4 * 512 * 512);
-		for (let i = 0; i < (512 * 512); ++i) {
+		for (let i: i32 = 0; i < (512 * 512); ++i) {
 			u8a[i * 4    ] = math_floor(pyimage[i                ] * 255);
 			u8a[i * 4 + 1] = math_floor(pyimage[i + 512 * 512    ] * 255);
 			u8a[i * 4 + 2] = math_floor(pyimage[i + 512 * 512 * 2] * 255);

+ 7 - 7
armorlab/Sources/nodes/tiling_node.ts

@@ -9,7 +9,7 @@ let tiling_node_prompt: string = "";
 let tiling_node_strength: f32 = 0.5;
 let tiling_node_auto: bool = true;
 
-function tiling_node_create(): tiling_node_t {
+function tiling_node_create(arg: any): tiling_node_t {
 	let n: float_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = tiling_node_get_as_image;
@@ -68,20 +68,20 @@ function tiling_node_sd_tiling(image: image_t, seed: i32, done: (img: image_t)=>
 	g2_end();
 
 	let u8a = u8_array_create(512 * 512);
-	for (let i = 0; i < 512 * 512; ++i) {
+	for (let i: i32 = 0; i < 512 * 512; ++i) {
 		let x = i % 512;
 		let y = math_floor(i / 512);
 		let l = y < 256 ? y : (511 - y);
 		u8a[i] = (x > 256 - l && x < 256 + l) ? 0 : 255;
 	}
-	// for (let i = 0; i < 512 * 512; ++i) u8a[i] = 255;
-	// for (let x = (256 - 32); x < (256 + 32); ++x) {
-	// 	for (let y = 0; y < 512; ++y) {
+	// for (let i: i32 = 0; i < 512 * 512; ++i) u8a[i] = 255;
+	// for (let x: i32 = (256 - 32); x < (256 + 32); ++x) {
+	// 	for (let y: i32 = 0; y < 512; ++y) {
 	// 		u8a[y * 512 + x] = 0;
 	// 	}
 	// }
-	// for (let x = 0; x < 512; ++x) {
-	// 	for (let y = (256 - 32); y < 256 + 32); ++y) {
+	// for (let x: i32 = 0; x < 512; ++x) {
+	// 	for (let y: i32 = (256 - 32); y < 256 + 32); ++y) {
 	// 		u8a[y * 512 + x] = 0;
 	// 	}
 	// }

+ 4 - 4
armorlab/Sources/nodes/upscale_node.ts

@@ -7,7 +7,7 @@ let upscale_node_temp: image_t = null;
 let upscale_node_image: image_t = null;
 let upscale_node_esrgan_blob: buffer_t;
 
-function upscale_node_create(): upscale_node_t {
+function upscale_node_create(arg: any): upscale_node_t {
 	let n: float_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = upscale_node_get_as_image;
@@ -63,7 +63,7 @@ function upscale_node_do_tile(source: image_t) {
 	let bytes_img = image_get_pixels(upscale_node_temp);
 	let u8a = new u8_array_t(bytes_img);
 	let f32a = f32_array_create(3 * size1w * size1h);
-	for (let i = 0; i < (size1w * size1h); ++i) {
+	for (let i: i32 = 0; i < (size1w * size1h); ++i) {
 		f32a[i                      ] = (u8a[i * 4    ] / 255);
 		f32a[i + size1w * size1w    ] = (u8a[i * 4 + 1] / 255);
 		f32a[i + size1w * size1w * 2] = (u8a[i * 4 + 2] / 255);
@@ -71,7 +71,7 @@ function upscale_node_do_tile(source: image_t) {
 
 	let esrgan2x_buf = krom_ml_inference(upscale_node_esrgan_blob, [f32a.buffer], [[1, 3, size1w, size1h]], [1, 3, size2w, size2h], config_raw.gpu_inference);
 	let esrgan2x = new f32_array_t(esrgan2x_buf);
-	for (let i = 0; i < esrgan2x.length; ++i) {
+	for (let i: i32 = 0; i < esrgan2x.length; ++i) {
 		if (esrgan2x[i] < 0) {
 			esrgan2x[i] = 0;
 		}
@@ -81,7 +81,7 @@ function upscale_node_do_tile(source: image_t) {
 	}
 
 	u8a = u8_array_create(4 * size2w * size2h);
-	for (let i = 0; i < (size2w * size2h); ++i) {
+	for (let i: i32 = 0; i < (size2w * size2h); ++i) {
 		u8a[i * 4    ] = math_floor(esrgan2x[i                      ] * 255);
 		u8a[i * 4 + 1] = math_floor(esrgan2x[i + size2w * size2w    ] * 255);
 		u8a[i * 4 + 2] = math_floor(esrgan2x[i + size2w * size2w * 2] * 255);

+ 5 - 5
armorlab/Sources/nodes/variance_node.ts

@@ -8,7 +8,7 @@ let variance_node_image: image_t = null;
 let variance_node_inst: variance_node_t = null;
 let variance_node_prompt: string = "";
 
-function variance_node_create(): variance_node_t {
+function variance_node_create(arg: any): variance_node_t {
 	let n: variance_node_t = {};
 	n.base = logic_node_create();
 	n.base.get_as_image = variance_node_get_as_image;
@@ -42,7 +42,7 @@ function variance_node_get_as_image(self: variance_node_t, from: i32, done: (img
 		let bytes_img = image_get_pixels(variance_node_temp);
 		let u8a = new u8_array_t(bytes_img);
 		let f32a = f32_array_create(3 * 512 * 512);
-		for (let i = 0; i < (512 * 512); ++i) {
+		for (let i: i32 = 0; i < (512 * 512); ++i) {
 			f32a[i                ] = (u8a[i * 4    ] / 255) * 2.0 - 1.0;
 			f32a[i + 512 * 512    ] = (u8a[i * 4 + 1] / 255) * 2.0 - 1.0;
 			f32a[i + 512 * 512 * 2] = (u8a[i * 4 + 2] / 255) * 2.0 - 1.0;
@@ -53,12 +53,12 @@ function variance_node_get_as_image(self: variance_node_t, from: i32, done: (img
 			let vae_encoder_blob: buffer_t = data_get_blob("models/sd_vae_encoder.quant.onnx");
 			let latents_buf = krom_ml_inference(vae_encoder_blob, [f32a.buffer], [[1, 3, 512, 512]], [1, 4, 64, 64], config_raw.gpu_inference);
 			let latents = new f32_array_t(latents_buf);
-			for (let i = 0; i < latents.length; ++i) {
+			for (let i: i32 = 0; i < latents.length; ++i) {
 				latents[i] = 0.18215 * latents[i];
 			}
 
 			let noise = f32_array_create(latents.length);
-			for (let i = 0; i < noise.length; ++i) {
+			for (let i: i32 = 0; i < noise.length; ++i) {
 				noise[i] = math_cos(2.0 * 3.14 * random_node_get_float()) * math_sqrt(-2.0 * math_log(random_node_get_float()));
 			}
 			let num_inference_steps = 50;
@@ -67,7 +67,7 @@ function variance_node_get_as_image(self: variance_node_t, from: i32, done: (img
 			let alphas_cumprod = text_to_photo_node_alphas_cumprod;
 			let sqrt_alpha_prod = math_pow(alphas_cumprod[timesteps], 0.5);
 			let sqrt_one_minus_alpha_prod = math_pow(1.0 - alphas_cumprod[timesteps], 0.5);
-			for (let i = 0; i < latents.length; ++i) {
+			for (let i: i32 = 0; i < latents.length; ++i) {
 				latents[i] = sqrt_alpha_prod * latents[i] + sqrt_one_minus_alpha_prod * noise[i];
 			}
 			let t_start = num_inference_steps - init_timestep;

+ 21 - 4
armorlab/Sources/nodes_brush.ts

@@ -24,10 +24,27 @@ let nodes_brush_list: zui_node_t[][] = [
 	]
 ];
 
-function nodes_brush_create_node(nodeType: string): zui_node_t {
-	for (let c of nodes_brush_list) {
-		for (let n of c) {
-			if (n.type == nodeType) {
+let nodes_brush_creates: map_t<string, any>;
+
+function nodes_brush_init() {
+	nodes_brush_creates = map_create();
+	map_set(nodes_brush_creates, "brush_output_node", brush_output_node_create);
+	map_set(nodes_brush_creates, "image_texture_node", image_texture_node_create);
+	map_set(nodes_brush_creates, "rgb_node", rgb_node_create);
+	map_set(nodes_brush_creates, "inpaint_node", inpaint_node_create);
+	map_set(nodes_brush_creates, "photo_to_pbr_node", photo_to_pbr_node_create);
+	map_set(nodes_brush_creates, "text_to_photo_node", text_to_photo_node_create);
+	map_set(nodes_brush_creates, "tiling_node", tiling_node_create);
+	map_set(nodes_brush_creates, "upscale_node", upscale_node_create);
+	map_set(nodes_brush_creates, "variance_node", variance_node_create);
+}
+
+function nodes_brush_create_node(node_type: string): zui_node_t {
+	for (let i: i32 = 0; i < nodes_brush_list.length; ++i) {
+		let c = nodes_brush_list[i];
+		for (let j: i32 = 0; j < c.length; ++j) {
+			let n = c[j];
+			if (n.type == node_type) {
 				let canvas = project_canvas;
 				let nodes = project_nodes;
 				let node = ui_nodes_make_node(n, nodes, canvas);

+ 2 - 2
armorlab/Sources/ui_nodes_ext.ts

@@ -97,12 +97,12 @@ function ui_nodes_ext_draw_buttons(ew: f32, start_y: f32) {
 						let vertices = g4_vertex_buffer_lock(g._.vertex_buffer);
 						if (ui_nodes_ext_last_vertices == null || buffer_view_size(ui_nodes_ext_last_vertices) != buffer_view_size(vertices)) {
 							ui_nodes_ext_last_vertices = buffer_view_create(buffer_create(buffer_view_size(vertices)));
-							for (let i = 0; i < math_floor(buffer_view_size(vertices) / 2); ++i) {
+							for (let i: i32 = 0; i < math_floor(buffer_view_size(vertices) / 2); ++i) {
 								buffer_view_set_i16(ui_nodes_ext_last_vertices, i * 2, buffer_view_get_i16(vertices, i * 2));
 							}
 						}
 						else {
-							for (let i = 0; i < math_floor(buffer_view_size(vertices) / 2); ++i) {
+							for (let i: i32 = 0; i < math_floor(buffer_view_size(vertices) / 2); ++i) {
 								buffer_view_set_i16(vertices, i * 2, buffer_view_get_i16(ui_nodes_ext_last_vertices, i * 2));
 							}
 						}

+ 6 - 3
armorpaint/Sources/import_folder.ts

@@ -11,7 +11,8 @@ function import_folder_run(path: string) {
 
 	let found_texture: bool = false;
 	// Import maps
-	for (let f of files) {
+	for (let i: i32 = 0; i < files.length; ++i) {
+		let f: string = files[i];
 		if (!path_is_texture(f)) {
 			continue;
 		}
@@ -68,13 +69,15 @@ function import_folder_run(path: string) {
 	let dirs: string[] = string_split(path, path_sep);
 	canvas.name = dirs[dirs.length - 1];
 	let nout: zui_node_t = null;
-	for (let n of canvas.nodes) {
+	for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+		let n: zui_node_t = canvas.nodes[i];
 		if (n.type == "OUTPUT_MATERIAL_PBR") {
 			nout = n;
 			break;
 		}
 	}
-	for (let n of canvas.nodes) {
+	for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+		let n: zui_node_t = canvas.nodes[i];
 		if (n.name == "RGB") {
 			zui_remove_node(n, canvas);
 			break;

+ 25 - 11
armorpaint/Sources/make_material.ts

@@ -7,7 +7,8 @@ let make_material_emis_used = false;
 let make_material_subs_used = false;
 
 function make_material_get_mout(): bool {
-	for (let n of ui_nodes_get_canvas_material().nodes) {
+	for (let i: i32 = 0; i < ui_nodes_get_canvas_material().nodes.length; ++i) {
+		let n: zui_node_t = ui_nodes_get_canvas_material().nodes[i];
 		if (n.type == "OUTPUT_MATERIAL_PBR") {
 			return true;
 		}
@@ -18,7 +19,8 @@ function make_material_get_mout(): bool {
 function make_material_parse_mesh_material() {
 	let m: material_data_t = project_materials[0].data;
 
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c: shader_context_t = m._.shader._.contexts[i];
 		if (c.name == "mesh") {
 			array_remove(m._.shader.contexts, c);
 			array_remove(m._.shader._.contexts, c);
@@ -105,7 +107,8 @@ function make_material_parse_mesh_material() {
 function make_material_parse_particle_material() {
 	let m: material_data_t = context_raw.particle_material;
 	let sc: shader_context_t = null;
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c: shader_context_t = m._.shader._.contexts[i];
 		if (c.name == "mesh") {
 			sc = c;
 			break;
@@ -129,7 +132,8 @@ function make_material_parse_mesh_preview_material(md: material_data_t = null) {
 
 	let m: material_data_t = md == null ? project_materials[0].data : md;
 	let scon: shader_context_t = null;
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c: shader_context_t = m._.shader._.contexts[i];
 		if (c.name == "mesh") {
 			scon = c;
 			break;
@@ -174,7 +178,8 @@ function make_material_make_voxel(m: material_data_t) {
 	let rebuild: bool = make_material_height_used;
 	if (config_raw.rp_gi != false && rebuild) {
 		let scon: shader_context_t = null;
-		for (let c of m._.shader._.contexts) {
+		for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+			let c: shader_context_t = m._.shader._.contexts[i];
 			if (c.name == "voxel") {
 				scon = c;
 				break;
@@ -201,7 +206,8 @@ function make_material_parse_paint_material(bake_previews = true) {
 	let m: material_data_t = project_materials[0].data;
 	// let scon: TShaderContext = null;
 	// let mcon: TMaterialContext = null;
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c: shader_context_t = m._.shader._.contexts[i];
 		if (c.name == "paint") {
 			array_remove(m._.shader.contexts, c);
 			array_remove(m._.shader._.contexts, c);
@@ -209,7 +215,8 @@ function make_material_parse_paint_material(bake_previews = true) {
 			break;
 		}
 	}
-	for (let c of m._.contexts) {
+	for (let i: i32 = 0; i < m._.contexts.length; ++i) {
+		let c: material_context_t = m._.contexts[i];
 		if (c.name == "paint") {
 			array_remove(m.contexts, c);
 			array_remove(m._.contexts, c);
@@ -250,9 +257,14 @@ function make_material_parse_paint_material(bake_previews = true) {
 
 function make_material_bake_node_previews() {
 	context_raw.node_previews_used = [];
-	if (context_raw.node_previews == null) context_raw.node_previews = map_create();
+	if (context_raw.node_previews == null) {
+		context_raw.node_previews = map_create();
+	}
 	make_material_traverse_nodes(ui_nodes_get_canvas_material().nodes, null, []);
-	for (let key of context_raw.node_previews.keys()) {
+
+	let keys: string[] = map_keys_to_array(context_raw.node_previews);
+	for (let i: i32 = 0; i < keys.length; ++i) {
+		let key: string = keys[i];
 		if (array_index_of(context_raw.node_previews_used, key) == -1) {
 			let image: image_t = map_get(context_raw.node_previews, key);
 			base_notify_on_next_frame(function() { image_unload(image); });
@@ -262,10 +274,12 @@ function make_material_bake_node_previews() {
 }
 
 function make_material_traverse_nodes(nodes: zui_node_t[], group: zui_node_canvas_t, parents: zui_node_t[]) {
-	for (let node of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let node: zui_node_t = nodes[i];
 		make_material_bake_node_preview(node, group, parents);
 		if (node.type == "GROUP") {
-			for (let g of project_material_groups) {
+			for (let j: i32 = 0; j < project_material_groups.length; ++j) {
+				let g: node_group_t = project_material_groups[j];
 				if (g.canvas.name == node.name) {
 					array_push(parents, node);
 					make_material_traverse_nodes(g.canvas.nodes, g.canvas, parents);

+ 16 - 8
armorpaint/Sources/make_mesh.ts

@@ -30,7 +30,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 		vert.n = true;
 		node_shader_write(vert, 'float height = 0.0;');
 		let num_layers: i32 = 0;
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (!slot_layer_is_visible(l) || !l.paint_height || !slot_layer_is_layer(l)) {
 				continue;
 			}
@@ -43,7 +44,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 			node_shader_write(vert, 'height += textureLod(texpaint_pack_vert' + l.id + ', tex, 0.0).a;');
 			let masks: slot_layer_t[] = slot_layer_get_masks(l);
 			if (masks != null) {
-				for (let m of masks) {
+				for (let i: i32 = 0; i < masks.length; ++i) {
+					let m: slot_layer_t = masks[i];
 					if (!slot_layer_is_visible(m)) {
 						continue;
 					}
@@ -128,7 +130,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 		}
 
 		if (context_raw.viewport_mode == viewport_mode_t.MASK && slot_layer_get_masks(context_raw.layer) != null) {
-			for (let m of slot_layer_get_masks(context_raw.layer)) {
+			for (let i: i32 = 0; i < slot_layer_get_masks(context_raw.layer).length; ++i) {
+				let m: slot_layer_t = slot_layer_get_masks(context_raw.layer)[i];
 				if (!slot_layer_is_visible(m)) {
 					continue;
 				}
@@ -150,7 +153,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 		let layers: slot_layer_t[] = [];
 		let start_count: i32 = texture_count;
 		let is_material_tool: bool = context_raw.tool == workspace_tool_t.MATERIAL;
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (is_material_tool && l != context_raw.layer) {
 				continue;
 			}
@@ -175,7 +179,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 
 		let last_pass: bool = layerPass == make_mesh_layer_pass_count - 1;
 
-		for (let l of layers) {
+		for (let i: i32 = 0; i < layers.length; ++i) {
+			let l: slot_layer_t = layers[i];
 			if (slot_layer_get_object_mask(l) > 0) {
 				node_shader_add_uniform(frag, 'int uid', '_uid');
 				if (slot_layer_get_object_mask(l) > project_paint_objects.length) { // Atlas
@@ -207,7 +212,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 			let masks: slot_layer_t[] = slot_layer_get_masks(l);
 			if (masks != null) {
 				let has_visible: bool = false;
-				for (let m of masks) {
+				for (let i: i32 = 0; i < masks.length; ++i) {
+					let m: slot_layer_t = masks[i];
 					if (slot_layer_is_visible(m)) {
 						has_visible = true;
 						break;
@@ -216,7 +222,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 				if (has_visible) {
 					let texpaint_mask: string = 'texpaint_mask' + l.id;
 					node_shader_write(frag, `float ${texpaint_mask} = 0.0;`);
-					for (let m of masks) {
+					for (let i: i32 = 0; i < masks.length; ++i) {
+						let m: slot_layer_t = masks[i];
 						if (!slot_layer_is_visible(m)) {
 							continue;
 						}
@@ -464,7 +471,8 @@ function make_mesh_run(data: material_t, layerPass = 0): node_shader_context_t {
 			}
 			else {
 				node_shader_write(frag, 'float mask_view = 0.0;');
-				for (let m of slot_layer_get_masks(context_raw.layer)) {
+				for (let i: i32 = 0; i < slot_layer_get_masks(context_raw.layer).length; ++i) {
+					let m: slot_layer_t = slot_layer_get_masks(context_raw.layer)[i];
 					if (!slot_layer_is_visible(m)) continue;
 					node_shader_write(frag, 'float mask_sample' + m.id + ' = textureLodShared(texpaint_view_mask' + m.id + ', texCoord, 0.0).r;');
 					node_shader_write(frag, 'mask_view = ' + make_material_blend_mode_mask(frag, m.blending, 'mask_view', 'mask_sample' + m.id, 'float(' + slot_layer_get_opacity(m) + ')') + ';');

+ 3 - 3
armorpaint/Sources/make_paint.ts

@@ -237,15 +237,15 @@ function make_paint_run(data: material_t, matcon: material_context_t): node_shad
 		if (context_raw.material.paint_subs) {
 			node_shader_write(frag, `float subs = ${subs};`);
 		}
-		if (parseFloat(height) != 0.0 && !make_material_height_used) {
+		if (parse_float(height) != 0.0 && !make_material_height_used) {
 			make_material_height_used = true;
 			// Height used for the first time, also rebuild vertex shader
 			return make_paint_run(data, matcon);
 		}
-		if (parseFloat(emis) != 0.0) {
+		if (parse_float(emis) != 0.0) {
 			make_material_emis_used = true;
 		}
-		if (parseFloat(subs) != 0.0) {
+		if (parse_float(subs) != 0.0) {
 			make_material_subs_used = true;
 		}
 	}

+ 1 - 1
armorpaint/Sources/nodes/brush_output_node.ts

@@ -4,7 +4,7 @@ type brush_output_node_t = {
 	Directional?: bool; // button 0
 };
 
-function brush_output_node_create(): brush_output_node_t {
+function brush_output_node_create(arg: any): brush_output_node_t {
 	let n: brush_output_node_t = {};
 	n.base = logic_node_create();
 	context_raw.run_brush = brush_output_node_run;

+ 1 - 1
armorpaint/Sources/nodes/input_node.ts

@@ -14,7 +14,7 @@ let input_node_lock_start_x: f32 = 0.0;
 let input_node_lock_start_y: f32 = 0.0;
 let input_node_registered: bool = false;
 
-function input_node_create(): input_node_t {
+function input_node_create(arg: any): input_node_t {
 	let n: float_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = input_node_get;

+ 1 - 1
armorpaint/Sources/nodes/tex_image_node.ts

@@ -5,7 +5,7 @@ type tex_image_node_t = {
 	color_space?: string;
 };
 
-function tex_image_node_create(): tex_image_node_t {
+function tex_image_node_create(arg: any): tex_image_node_t {
 	let n: tex_image_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = tex_image_node_get;

+ 20 - 2
armorpaint/Sources/nodes_brush.ts

@@ -17,9 +17,27 @@ let nodes_brush_list: zui_node_t[][] = [
 	]
 ];
 
+let nodes_brush_creates: map_t<string, any>;
+
+function nodes_brush_init() {
+	nodes_brush_creates = map_create();
+	map_set(nodes_brush_creates, "brush_output_node", brush_output_node_create);
+	map_set(nodes_brush_creates, "tex_image_node", tex_image_node_create);
+	map_set(nodes_brush_creates, "input_node", input_node_create);
+	map_set(nodes_brush_creates, "math_node", math_node_create);
+	map_set(nodes_brush_creates, "random_node", random_node_create);
+	map_set(nodes_brush_creates, "separate_vector_node", separate_vector_node_create);
+	map_set(nodes_brush_creates, "time_node", time_node_create);
+	map_set(nodes_brush_creates, "float_node", float_node_create);
+	map_set(nodes_brush_creates, "vector_node", vector_node_create);
+	map_set(nodes_brush_creates, "vector_math_node", vector_math_node_create);
+}
+
 function nodes_brush_create_node(node_type: string): zui_node_t {
-	for (let c of nodes_brush_list) {
-		for (let n of c) {
+	for (let i: i32 = 0; i < nodes_brush_list.length; ++i) {
+		let c: zui_node_t[] = nodes_brush_list[i];
+		for (let i: i32 = 0; i < c.length; ++i) {
+			let n: zui_node_t = c[i];
 			if (n.type == node_type) {
 				let canvas: zui_node_canvas_t = context_raw.brush.canvas;
 				let nodes: zui_nodes_t = context_raw.brush.nodes;

+ 8 - 4
armorpaint/Sources/render_path_paint.ts

@@ -221,7 +221,8 @@ function render_path_paint_commands_paint(dilation = true) {
 				if (context_raw.picker_select_material && context_raw.color_picker_callback == null) {
 					// matid % 3 == 0 - normal, 1 - emission, 2 - subsurface
 					let matid: i32 = math_floor((buffer_view_get_u8(b, 3) - (buffer_view_get_u8(b, 3) % 3)) / 3);
-					for (let m of project_materials) {
+					for (let i: i32 = 0; i < project_materials.length; ++i) {
+						let m: slot_material_t = project_materials[i];
 						if (m.id == matid) {
 							context_set_material(m);
 							context_raw.materialid_picked = matid;
@@ -264,7 +265,8 @@ function render_path_paint_commands_paint(dilation = true) {
 			if (is_mask) {
 				let ptid: i32 = context_raw.layer.parent.id;
 				if (slot_layer_is_group(context_raw.layer.parent)) { // Group mask
-					for (let c of slot_layer_get_children(context_raw.layer.parent)) {
+					for (let i: i32 = 0; i < slot_layer_get_children(context_raw.layer.parent).length; ++i) {
+						let c: slot_layer_t = slot_layer_get_children(context_raw.layer.parent)[i];
 						ptid = c.id;
 						break;
 					}
@@ -751,7 +753,8 @@ function render_path_paint_draw() {
 					context_raw.merged_object.base.visible = false;
 				}
 
-				for (let p of project_paint_objects) {
+				for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+					let p: mesh_object_t = project_paint_objects[i];
 					context_select_paint_object(p);
 					render_path_paint_commands_paint();
 				}
@@ -805,7 +808,8 @@ function render_path_paint_set_plane_mesh() {
 	context_raw.paint2d_view = true;
 	render_path_paint_painto = context_raw.paint_object;
 	render_path_paint_visibles = [];
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		array_push(render_path_paint_visibles, p.base.visible);
 		p.base.visible = false;
 	}

+ 15 - 10
armorpaint/Sources/slot_brush.ts

@@ -1,18 +1,23 @@
 
-class slot_brush_t {
-	nodes: zui_nodes_t = zui_nodes_create();
-	canvas: zui_node_canvas_t;
-	image: image_t = null; // 200px
-	image_icon: image_t = null; // 50px
-	preview_ready: bool = false;
-	id: i32 = 0;
-}
+type slot_brush_t = {
+	nodes?: zui_nodes_t;
+	canvas?: zui_node_canvas_t;
+	image?: image_t; // 200px
+	image_icon?: image_t; // 50px
+	preview_ready?: bool;
+	id?: i32;
+};
 
 let slot_brush_default_canvas: buffer_t = null;
 
 function slot_brush_create(c: zui_node_canvas_t = null): slot_brush_t {
-	let raw: slot_brush_t = new slot_brush_t();
-	for (let brush of project_brushes) {
+	let raw: slot_brush_t = {};
+	raw.nodes = zui_nodes_create();
+	raw.preview_ready = false;
+	raw.id = 0;
+
+	for (let i: i32 = 0; i < project_brushes.length; ++i) {
+		let brush: slot_brush_t = project_brushes[i];
 		if (brush.id >= raw.id) {
 			raw.id = brush.id + 1;
 		}

+ 15 - 11
armorpaint/Sources/slot_font.ts

@@ -1,16 +1,20 @@
 
-class slot_font_t {
-	image: image_t = null; // 200px
-	preview_ready: bool = false;
-	id: i32 = 0;
-	font: g2_font_t;
-	name: string;
-	file: string;
-}
+type slot_font_t = {
+	image?: image_t; // 200px
+	preview_ready?: bool;
+	id?: i32;
+	font?: g2_font_t;
+	name?: string;
+	file?: string;
+};
+
+function slot_font_create(name: string, font: g2_font_t, file: string = ""): slot_font_t {
+	let raw: slot_font_t = {};
+	raw.preview_ready = false;
+	raw.id = 0;
 
-function slot_font_create(name: string, font: g2_font_t, file = ""): slot_font_t {
-	let raw: slot_font_t = new slot_font_t();
-	for (let slot of project_fonts) {
+	for (let i: i32 = 0; i < project_fonts.length; ++i) {
+		let slot: slot_font_t = project_fonts[i];
 		if (slot.id >= raw.id) {
 			raw.id = slot.id + 1;
 		}

+ 95 - 62
armorpaint/Sources/slot_layer.ts

@@ -1,45 +1,67 @@
 
-class slot_layer_t {
-	id: i32 = 0;
-	name: string;
-	ext: string = "";
-	visible: bool = true;
-	parent: slot_layer_t = null; // Group (for layers) or layer (for masks)
-
-	texpaint: image_t = null; // Base or mask
+type slot_layer_t = {
+	id?: i32;
+	name?: string;
+	ext?: string;
+	visible?: bool;
+	parent?: slot_layer_t; // Group (for layers) or layer (for masks)
+	texpaint?: image_t; // Base or mask
 	///if is_paint
-	texpaint_nor: image_t = null;
-	texpaint_pack: image_t = null;
-	texpaint_preview: image_t = null; // Layer preview
+	texpaint_nor?: image_t;
+	texpaint_pack?: image_t;
+	texpaint_preview?: image_t; // Layer preview
 	///end
-
-	mask_opacity: f32 = 1.0; // Opacity mask
-	fill_layer: slot_material_t = null;
-	show_panel: bool = true;
-	blending = blend_type_t.MIX;
-	object_mask: i32 = 0;
-	scale: f32 = 1.0;
-	angle: f32 = 0.0;
-	uv_type = uv_type_t.UVMAP;
-	paint_base: bool = true;
-	paint_opac: bool = true;
-	paint_occ: bool = true;
-	paint_rough: bool = true;
-	paint_met: bool = true;
-	paint_nor: bool = true;
-	paint_nor_blend: bool = true;
-	paint_height: bool = true;
-	paint_height_blend: bool = true;
-	paint_emis: bool = true;
-	paint_subs: bool = true;
-	decal_mat: mat4_t = mat4_identity(); // Decal layer
-}
+	mask_opacity?: f32; // Opacity mask
+	fill_layer?: slot_material_t;
+	show_panel?: bool;
+	blending?: blend_type_t;
+	object_mask?: i32;
+	scale?: f32;
+	angle?: f32;
+	uv_type?: uv_type_t;
+	paint_base?: bool;
+	paint_opac?: bool;
+	paint_occ?: bool;
+	paint_rough?: bool;
+	paint_met?: bool;
+	paint_nor?: bool;
+	paint_nor_blend?: bool;
+	paint_height?: bool;
+	paint_height_blend?: bool;
+	paint_emis?: bool;
+	paint_subs?: bool;
+	decal_mat?: mat4_t; // Decal layer
+};
 
 function slot_layer_create(ext: string = "", type: layer_slot_type_t = layer_slot_type_t.LAYER, parent: slot_layer_t = null): slot_layer_t {
-	let raw: slot_layer_t = new slot_layer_t();
+	let raw: slot_layer_t = {};
+	raw.id = 0;
+	raw.ext = "";
+	raw.visible = true;
+	raw.mask_opacity = 1.0; // Opacity mask
+	raw.show_panel = true;
+	raw.blending = blend_type_t.MIX;
+	raw.object_mask = 0;
+	raw.scale = 1.0;
+	raw.angle = 0.0;
+	raw.uv_type = uv_type_t.UVMAP;
+	raw.paint_base = true;
+	raw.paint_opac = true;
+	raw.paint_occ = true;
+	raw.paint_rough = true;
+	raw.paint_met = true;
+	raw.paint_nor = true;
+	raw.paint_nor_blend = true;
+	raw.paint_height = true;
+	raw.paint_height_blend = true;
+	raw.paint_emis = true;
+	raw.paint_subs = true;
+	raw.decal_mat = mat4_identity(); // Decal layer
+
 	if (ext == "") {
 		raw.id = 0;
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (l.id >= raw.id) {
 				raw.id = l.id + 1;
 			}
@@ -123,7 +145,8 @@ function slot_layer_delete(raw: slot_layer_t) {
 	if (slot_layer_is_layer(raw)) {
 		let masks: slot_layer_t[] = slot_layer_get_masks(raw, false); // Prevents deleting group masks
 		if (masks != null) {
-			for (let m of masks) {
+			for (let i: i32 = 0; i < masks.length; ++i) {
+				let m: slot_layer_t = masks[i];
 				slot_layer_delete(m);
 			}
 		}
@@ -131,13 +154,15 @@ function slot_layer_delete(raw: slot_layer_t) {
 	else if (slot_layer_is_group(raw)) {
 		let children: slot_layer_t[] = slot_layer_get_children(raw);
 		if (children != null) {
-			for (let c of children) {
+			for (let i: i32 = 0; i < children.length; ++i) {
+				let c: slot_layer_t = children[i];
 				slot_layer_delete(c);
 			}
 		}
 		let masks: slot_layer_t[] = slot_layer_get_masks(raw);
 		if (masks != null) {
-			for (let m of masks) {
+			for (let i: i32 = 0; i < masks.length; ++i) {
+				let m: slot_layer_t = masks[i];
 				slot_layer_delete(m);
 			}
 		}
@@ -269,7 +294,8 @@ function slot_layer_apply_mask(raw: slot_layer_t) {
 		slot_layer_to_paint_layer(raw.parent);
 	}
 	if (slot_layer_is_group(raw.parent)) {
-		for (let c of slot_layer_get_children(raw.parent)) {
+		for (let i: i32 = 0; i < slot_layer_get_children(raw.parent).length; ++i) {
+			let c: slot_layer_t = slot_layer_get_children(raw.parent)[i];
 			base_apply_mask(c, raw);
 		}
 	}
@@ -453,7 +479,8 @@ function slot_layer_is_visible(raw: slot_layer_t): bool {
 
 function slot_layer_get_children(raw: slot_layer_t): slot_layer_t[] {
 	let children: slot_layer_t[] = null; // Child layers of a group
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (l.parent == raw && slot_layer_is_layer(l)) {
 			if (children == null) {
 				children = [];
@@ -466,7 +493,8 @@ function slot_layer_get_children(raw: slot_layer_t): slot_layer_t[] {
 
 function slot_layer_get_recursive_children(raw: slot_layer_t): slot_layer_t[] {
 	let children: slot_layer_t[] = null;
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (l.parent == raw) { // Child layers and group masks
 			if (children == null) {
 				children = [];
@@ -490,7 +518,8 @@ function slot_layer_get_masks(raw: slot_layer_t, includeGroupMasks = true): slot
 
 	let children: slot_layer_t[] = null;
 	// Child masks of a layer
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (l.parent == raw && slot_layer_is_mask(l)) {
 			if (children == null) {
 				children = [];
@@ -501,7 +530,8 @@ function slot_layer_get_masks(raw: slot_layer_t, includeGroupMasks = true): slot
 	// Child masks of a parent group
 	if (includeGroupMasks) {
 		if (raw.parent != null && slot_layer_is_group(raw.parent)) {
-			for (let l of project_layers) {
+			for (let i: i32 = 0; i < project_layers.length; ++i) {
+				let l: slot_layer_t = project_layers[i];
 				if (l.parent == raw.parent && slot_layer_is_mask(l)) {
 					if (children == null) {
 						children = [];
@@ -516,14 +546,16 @@ function slot_layer_get_masks(raw: slot_layer_t, includeGroupMasks = true): slot
 
 function slot_layer_has_masks(raw: slot_layer_t, includeGroupMasks = true): bool {
 	// Layer mask
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (l.parent == raw && slot_layer_is_mask(l)) {
 			return true;
 		}
 	}
 	// Group mask
 	if (includeGroupMasks && raw.parent != null && slot_layer_is_group(raw.parent)) {
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (l.parent == raw.parent && slot_layer_is_mask(l)) {
 				return true;
 			}
@@ -608,12 +640,12 @@ function slot_layer_can_move(raw: slot_layer_t, to: i32): bool {
 		return false;
 	}
 
-	// If the layer is moved up, all layers between the old position and the new one move one down.
-	// The layers above the new position stay where they are.
-	// If the new position is on top or on bottom no upper resp. lower layer exists.
+	// If the layer is moved up, all layers between the old position and the new one move one down
+	// The layers above the new position stay where they are
+	// If the new position is on top or on bottom no upper resp. lower layer exists
 	let new_upper_layer: slot_layer_t = delta > 0 ? (to < project_layers.length - 1 ? project_layers[to + 1] : null) : project_layers[to];
 
-	// Group or layer is collapsed so we check below and update the upper layer.
+	// Group or layer is collapsed so we check below and update the upper layer
 	if (new_upper_layer != null && !new_upper_layer.show_panel) {
 		let children: slot_layer_t[] = slot_layer_get_recursive_children(new_upper_layer);
 		to -= children != null ? children.length : 0;
@@ -624,50 +656,50 @@ function slot_layer_can_move(raw: slot_layer_t, to: i32): bool {
 	let new_lower_layer: slot_layer_t = delta > 0 ? project_layers[to] : (to > 0 ? project_layers[to - 1] : null);
 
 	if (slot_layer_is_mask(raw)) {
-		// Masks can not be on top.
+		// Masks can not be on top
 		if (new_upper_layer == null) {
 			return false;
 		}
-		// Masks should not be placed below a collapsed group. This condition can be savely removed.
+		// Masks should not be placed below a collapsed group - this condition can be savely removed
 		if (slot_layer_is_in_group(new_upper_layer) && !slot_layer_get_containing_group(new_upper_layer).show_panel) {
 			return false;
 		}
-		// Masks should not be placed below a collapsed layer. This condition can be savely removed.
+		// Masks should not be placed below a collapsed layer - this condition can be savely removed
 		if (slot_layer_is_mask(new_upper_layer) && !new_upper_layer.parent.show_panel) {
 			return false;
 		}
 	}
 
 	if (slot_layer_is_layer(raw)) {
-		// Layers can not be moved directly below its own mask(s).
+		// Layers can not be moved directly below its own mask(s)
 		if (new_upper_layer != null && slot_layer_is_mask(new_upper_layer) && new_upper_layer.parent == raw) {
 			return false;
 		}
-		// Layers can not be placed above a mask as the mask would be reparented.
+		// Layers can not be placed above a mask as the mask would be reparented
 		if (new_lower_layer != null && slot_layer_is_mask(new_lower_layer)) {
 			return false;
 		}
 	}
 
-	// Currently groups can not be nested. Thus valid positions for groups are:
+	// Currently groups can not be nested - thus valid positions for groups are:
 	if (slot_layer_is_group(raw)) {
-		// At the top.
+		// At the top
 		if (new_upper_layer == null) {
 			return true;
 		}
-		// NOT below its own children.
+		// NOT below its own children
 		if (slot_layer_get_containing_group(new_upper_layer) == raw) {
 			return false;
 		}
-		// At the bottom.
+		// At the bottom
 		if (new_lower_layer == null) {
 			return true;
 		}
-		// Above a group.
+		// Above a group
 		if (slot_layer_is_group(new_lower_layer)) {
 			return true;
 		}
-		// Above a non-grouped layer.
+		// Above a non-grouped layer
 		if (slot_layer_is_layer(new_lower_layer) && !slot_layer_is_in_group(new_lower_layer)) {
 			return true;
 		}
@@ -689,7 +721,7 @@ function slot_layer_move(raw: slot_layer_t, to: i32) {
 	let delta: i32 = to - old_index;
 	let new_upper_layer: slot_layer_t = delta > 0 ? (to < project_layers.length - 1 ? project_layers[to + 1] : null) : project_layers[to];
 
-	// Group or layer is collapsed so we check below and update the upper layer.
+	// Group or layer is collapsed so we check below and update the upper layer
 	if (new_upper_layer != null && !new_upper_layer.show_panel) {
 		let children: slot_layer_t[] = slot_layer_get_recursive_children(new_upper_layer);
 		to -= children != null ? children.length : 0;
@@ -765,7 +797,8 @@ function slot_layer_move(raw: slot_layer_t, to: i32) {
 		}
 	}
 
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		tab_layers_remap_layer_pointers(m.canvas.nodes, tab_layers_fill_layer_map(pointers));
 	}
 }

+ 34 - 21
armorpaint/Sources/slot_material.ts

@@ -1,29 +1,42 @@
 
-class slot_material_t {
-	nodes: zui_nodes_t = zui_nodes_create();
-	canvas: zui_node_canvas_t;
-	image: image_t = null;
-	image_icon: image_t = null;
-	preview_ready: bool = false;
-	data: material_data_t;
-	id: i32 = 0;
-
-	paint_base: bool = true;
-	paint_opac: bool = true;
-	paint_occ: bool = true;
-	paint_rough: bool = true;
-	paint_met: bool = true;
-	paint_nor: bool = true;
-	paint_height: bool = true;
-	paint_emis: bool = true;
-	paint_subs: bool = true;
-}
+type slot_material_t = {
+	nodes?: zui_nodes_t;
+	canvas?: zui_node_canvas_t;
+	image?: image_t;
+	image_icon?: image_t;
+	preview_ready?: bool;
+	data?: material_data_t;
+	id?: i32;
+	paint_base?: bool;
+	paint_opac?: bool;
+	paint_occ?: bool;
+	paint_rough?: bool;
+	paint_met?: bool;
+	paint_nor?: bool;
+	paint_height?: bool;
+	paint_emis?: bool;
+	paint_subs?: bool;
+};
 
 let slot_material_default_canvas: buffer_t = null;
 
 function slot_material_create(m: material_data_t = null, c: zui_node_canvas_t = null): slot_material_t {
-	let raw: slot_material_t = new slot_material_t();
-	for (let mat of project_materials) {
+	let raw: slot_material_t = {};
+	raw.nodes = zui_nodes_create();
+	raw.preview_ready = false;
+	raw.id = 0;
+	raw.paint_base = true;
+	raw.paint_opac = true;
+	raw.paint_occ = true;
+	raw.paint_rough = true;
+	raw.paint_met = true;
+	raw.paint_nor = true;
+	raw.paint_height = true;
+	raw.paint_emis = true;
+	raw.paint_subs = true;
+
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let mat: slot_material_t = project_materials[i];
 		if (mat.id >= raw.id) {
 			raw.id = mat.id + 1;
 		}

+ 35 - 17
armorpaint/Sources/tab_layers.ts

@@ -156,7 +156,8 @@ function tab_layers_button_new(text: string) {
 				array_remove(project_layers, group);
 				array_insert(project_layers, array_index_of(project_layers, l) + 1, group);
 				l.parent = group;
-				for (let m of project_materials) {
+				for (let i: i32 = 0; i < project_materials.length; ++i) {
+					let m: slot_material_t = project_materials[i];
 					tab_layers_remap_layer_pointers(m.canvas.nodes, tab_layers_fill_layer_map(pointers));
 				}
 				context_set_layer(group);
@@ -169,12 +170,14 @@ function tab_layers_button_new(text: string) {
 
 function tab_layers_combo_filter() {
 	let ar: string[] = [tr("All")];
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		array_push(ar, p.base.name);
 	}
 	let atlases: string[] = project_get_used_atlases();
 	if (atlases != null) {
-		for (let a of atlases) {
+		for (let i: i32 = 0; i < atlases.length; ++i) {
+			let a: string = atlases[i];
 			array_push(ar, a);
 		}
 	}
@@ -182,7 +185,8 @@ function tab_layers_combo_filter() {
 	filter_handle.position = context_raw.layer_filter;
 	context_raw.layer_filter = zui_combo(filter_handle, ar, tr("Filter"), false, zui_align_t.LEFT);
 	if (filter_handle.changed) {
-		for (let p of project_paint_objects) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+			let p: mesh_object_t = project_paint_objects[i];
 			p.base.visible = context_raw.layer_filter == 0 || p.base.name == ar[context_raw.layer_filter] || project_is_atlas_object(p);
 		}
 		if (context_raw.layer_filter == 0 && context_raw.merged_object_is_atlas) { // All
@@ -190,7 +194,8 @@ function tab_layers_combo_filter() {
 		}
 		else if (context_raw.layer_filter > project_paint_objects.length) { // Atlas
 			let visibles: mesh_object_t[] = [];
-			for (let p of project_paint_objects) {
+			for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+				let p: mesh_object_t = project_paint_objects[i];
 				if (p.base.visible) {
 					array_push(visibles, p);
 				}
@@ -207,7 +212,8 @@ function tab_layers_combo_filter() {
 }
 
 function tab_layers_remap_layer_pointers(nodes: zui_node_t[], pointer_map: map_t<i32, i32>) {
-	for (let n of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let n: zui_node_t = nodes[i];
 		if (n.type == "LAYER" || n.type == "LAYER_MASK") {
 			let i: any = n.buttons[0].default_value;
 			if (pointer_map.has(i)) {
@@ -227,7 +233,9 @@ function tab_layers_init_layer_map(): map_t<slot_layer_t, i32> {
 
 function tab_layers_fill_layer_map(map: map_t<slot_layer_t, i32>): map_t<i32, i32> {
 	let res: map_t<i32, i32> = map_create();
-	for (let l of map.keys()) {
+	let keys: string[] = map_keys_to_array(map);
+	for (let i: i32 = 0; i < keys.length; ++i) {
+		let l: string = keys[i];
 		map_set(res, map_get(map, l), array_index_of(project_layers, l) > -1 ? array_index_of(project_layers, l) : 9999);
 	}
 	return res;
@@ -478,12 +486,14 @@ function tab_layers_draw_layer_slot_full(l: slot_layer_t, i: i32) {
 
 function tab_layers_combo_object(ui: zui_t, l: slot_layer_t, label = false): zui_handle_t {
 	let ar: string[] = [tr("Shared")];
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		array_push(ar, p.base.name);
 	}
 	let atlases: string[] = project_get_used_atlases();
 	if (atlases != null) {
-		for (let a of atlases) {
+		for (let i: i32 = 0; i < atlases.length; ++i) {
+			let a: string = atlases[i];
 			array_push(ar, a);
 		}
 	}
@@ -824,7 +834,8 @@ function tab_layers_draw_layer_context_menu(l: slot_layer_t, mini: bool) {
 					slot_layer_clear(l);
 				}
 				else {
-					for (let c of slot_layer_get_children(l)) {
+					for (let i: i32 = 0; i < slot_layer_get_children(l).length; ++i) {
+						let c: slot_layer_t = slot_layer_get_children(l)[i];
 						context_raw.layer = c;
 						history_clear_layer();
 						slot_layer_clear(c);
@@ -1065,16 +1076,19 @@ function tab_layers_delete_layer(l: slot_layer_t) {
 	let pointers: map_t<slot_layer_t, i32> = tab_layers_init_layer_map();
 
 	if (slot_layer_is_layer(l) && slot_layer_has_masks(l, false)) {
-		for (let m of slot_layer_get_masks(l, false)) {
+		for (let i: i32 = 0; i < slot_layer_get_masks(l, false).length; ++i) {
+			let m: slot_layer_t = slot_layer_get_masks(l, false)[i];
 			context_raw.layer = m;
 			history_delete_layer();
 			slot_layer_delete(m);
 		}
 	}
 	if (slot_layer_is_group(l)) {
-		for (let c of slot_layer_get_children(l)) {
+		for (let i: i32 = 0; i < slot_layer_get_children(l).length; ++i) {
+			let c: slot_layer_t = slot_layer_get_children(l)[i];
 			if (slot_layer_has_masks(c, false)) {
-				for (let m of slot_layer_get_masks(c, false)) {
+				for (let i: i32 = 0; i < slot_layer_get_masks(c, false).length; ++i) {
+					let m: slot_layer_t = slot_layer_get_masks(c, false)[i];
 					context_raw.layer = m;
 					history_delete_layer();
 					slot_layer_delete(m);
@@ -1085,7 +1099,8 @@ function tab_layers_delete_layer(l: slot_layer_t) {
 			slot_layer_delete(c);
 		}
 		if (slot_layer_has_masks(l)) {
-			for (let m of slot_layer_get_masks(l)) {
+			for (let i: i32 = 0; i < slot_layer_get_masks(l).length; ++i) {
+				let m: slot_layer_t = slot_layer_get_masks(l)[i];
 				context_raw.layer = m;
 				history_delete_layer();
 				slot_layer_delete(m);
@@ -1107,7 +1122,8 @@ function tab_layers_delete_layer(l: slot_layer_t) {
 		let g: slot_layer_t = slot_layer_get_containing_group(l);
 		// Maybe some group masks are left
 		if (slot_layer_has_masks(g)) {
-			for (let m of slot_layer_get_masks(g)) {
+			for (let i: i32 = 0; i < slot_layer_get_masks(g).length; ++i) {
+				let m: slot_layer_t = slot_layer_get_masks(g)[i];
 				context_raw.layer = m;
 				history_delete_layer();
 				slot_layer_delete(m);
@@ -1118,7 +1134,8 @@ function tab_layers_delete_layer(l: slot_layer_t) {
 		slot_layer_delete(l.parent);
 	}
 	context_raw.ddirty = 2;
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		tab_layers_remap_layer_pointers(m.canvas.nodes, tab_layers_fill_layer_map(pointers));
 	}
 }
@@ -1130,7 +1147,8 @@ function tab_layers_can_delete(l: slot_layer_t) {
 		return true;
 	}
 
-	for (let slot of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let slot: slot_layer_t = project_layers[i];
 		if (slot_layer_is_layer(slot)) {
 			++num_layers;
 		}

+ 7 - 7
armorsculpt/Sources/export_obj.ts

@@ -1,6 +1,6 @@
 
 function export_obj_write_string(out: i32[], str: string) {
-	for (let i = 0; i < str.length; ++i) {
+	for (let i: i32 = 0; i < str.length; ++i) {
 		array_push(out, char_code_at(str, i));
 	}
 }
@@ -16,7 +16,7 @@ function export_obj_run(path: string, paint_objects: mesh_object_t[], apply_disp
 	let inda = mesh.index_arrays[0].values;
 
 	let posa: i16_array_t = i16_array_create(inda.length * 4);
-	for (let i = 0; i < inda.length; ++i) {
+	for (let i: i32 = 0; i < inda.length; ++i) {
 		let index = inda[i];
 		posa[index * 4    ] = math_floor(buffer_view_get_f32(pixelsView, i * 16    ) * 32767);
 		posa[index * 4 + 1] = math_floor(buffer_view_get_f32(pixelsView, i * 16 + 4) * 32767);
@@ -24,7 +24,7 @@ function export_obj_run(path: string, paint_objects: mesh_object_t[], apply_disp
 	}
 
 	let poff = 0;
-	// for (let p of paintObjects) {
+	// for (let p of paint_objects) {
 		let p = paint_objects[0];
 		// let mesh = p.data.raw;
 		let inv = 1 / 32767;
@@ -38,9 +38,9 @@ function export_obj_run(path: string, paint_objects: mesh_object_t[], apply_disp
 		let posmap: map_t<i32, i32> = map_create();
 
 		let pi = 0;
-		for (let i = 0; i < len; ++i) {
+		for (let i: i32 = 0; i < len; ++i) {
 			let found = false;
-			for (let j = 0; j < pi; ++j) {
+			for (let j: i32 = 0; j < pi; ++j) {
 				if (posa2[j * 3    ] == posa[i * 4    ] &&
 					posa2[j * 3 + 1] == posa[i * 4 + 1] &&
 					posa2[j * 3 + 2] == posa[i * 4 + 2]) {
@@ -59,7 +59,7 @@ function export_obj_run(path: string, paint_objects: mesh_object_t[], apply_disp
 		}
 
 		export_obj_write_string(o, "o " + p.base.name + "\n");
-		for (let i = 0; i < pi; ++i) {
+		for (let i: i32 = 0; i < pi; ++i) {
 			export_obj_write_string(o, "v ");
 			let vx = posa2[i * 3] * sc + "";
 			export_obj_write_string(o, substring(vx, 0, string_index_of(vx, ".") + 7));
@@ -73,7 +73,7 @@ function export_obj_run(path: string, paint_objects: mesh_object_t[], apply_disp
 		}
 
 		// let inda = mesh.index_arrays[0].values;
-		for (let i = 0; i < math_floor(inda.length / 3); ++i) {
+		for (let i: i32 = 0; i < math_floor(inda.length / 3); ++i) {
 			let pi1 = map_get(posmap, inda[i * 3    ]) + 1 + poff;
 			let pi2 = map_get(posmap, inda[i * 3 + 1]) + 1 + poff;
 			let pi3 = map_get(posmap, inda[i * 3 + 2]) + 1 + poff;

+ 9 - 7
armorsculpt/Sources/import_mesh.ts

@@ -56,7 +56,8 @@ function import_mesh_finish_import() {
 		});
 
 		// No mask by default
-		for (let p of project_paint_objects) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+			let p = project_paint_objects[i];
 			p.base.visible = true;
 		}
 		if (context_raw.merged_object == null) {
@@ -97,7 +98,7 @@ function import_mesh_make_mesh(mesh: any, path: string) {
 		context_raw.paint_object = context_main_object();
 
 		context_select_paint_object(context_main_object());
-		for (let i = 0; i < project_paint_objects.length; ++i) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
 			let p = project_paint_objects[i];
 			if (p == context_raw.paint_object) {
 				continue;
@@ -131,12 +132,12 @@ function import_mesh_make_mesh(mesh: any, path: string) {
 		ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
 		ui_base_hwnds[tab_area_t.SIDEBAR1].redraws = 2;
 
-		// Wait for addMesh calls to finish
+		// Wait for add_mesh calls to finish
 		app_notify_on_init(import_mesh_finish_import);
 
 		base_notify_on_next_frame(function () {
 			let f32 = f32_array_create(config_get_texture_res_x() * config_get_texture_res_y() * 4);
-			for (let i = 0; i < math_floor(mesh.inda.length); ++i) {
+			for (let i: i32 = 0; i < math_floor(mesh.inda.length); ++i) {
 				let index = mesh.inda[i];
 				f32[i * 4]     = mesh.posa[index * 4]     / 32767;
 				f32[i * 4 + 1] = mesh.posa[index * 4 + 1] / 32767;
@@ -158,7 +159,7 @@ function import_mesh_make_mesh(mesh: any, path: string) {
 
 function import_mesh_add_mesh(mesh: any) {
 
-	let _addMesh = function () {
+	let _add_mesh = function () {
 		let raw = import_mesh_raw_mesh(mesh);
 		if (mesh.cola != null) {
 			array_push(raw.vertex_arrays, { values: mesh.cola, attrib: "col", data: "short4norm" });
@@ -171,7 +172,8 @@ function import_mesh_add_mesh(mesh: any) {
 		object.skip_context = "paint";
 
 		// Ensure unique names
-		for (let p of project_paint_objects) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+			let p = project_paint_objects[i];
 			if (p.base.name == object.base.name) {
 				p.base.name += ".001";
 				p.data._.handle += ".001";
@@ -188,7 +190,7 @@ function import_mesh_add_mesh(mesh: any) {
 		ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
 	}
 
-	_addMesh();
+	_add_mesh();
 }
 
 function import_mesh_raw_mesh(mesh: any): mesh_data_t {

+ 29 - 15
armorsculpt/Sources/make_material.ts

@@ -7,7 +7,8 @@ let make_material_emis_used: bool = false;
 let make_material_subs_used: bool = false;
 
 function make_material_get_mout(): bool {
-	for (let n of ui_nodes_get_canvas_material().nodes) {
+	for (let i: i32 = 0; i < ui_nodes_get_canvas_material().nodes.length; ++i) {
+		let n = ui_nodes_get_canvas_material().nodes[i];
 		if (n.type == "OUTPUT_MATERIAL_PBR") {
 			return true;
 		}
@@ -18,7 +19,8 @@ function make_material_get_mout(): bool {
 function make_material_parse_mesh_material() {
 	let m = project_materials[0].data;
 
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c = m._.shader._.contexts[i];
 		if (c.name == "mesh") {
 			array_remove(m._.shader.contexts, c);
 			array_remove(m._.shader._.contexts, c);
@@ -31,7 +33,7 @@ function make_material_parse_mesh_material() {
 		let i = 0;
 		while (i < m._.shader._.contexts.length) {
 			let c = m._.shader._.contexts[i];
-			for (let j = 1; j < make_mesh_layer_pass_count; ++j) {
+			for (let j: i32 = 1; j < make_mesh_layer_pass_count; ++j) {
 				if (c.name == "mesh" + j) {
 					array_remove(m._.shader.contexts, c);
 					array_remove(m._.shader._.contexts, c);
@@ -46,7 +48,7 @@ function make_material_parse_mesh_material() {
 		i = 0;
 		while (i < m.contexts.length) {
 			let c = m.contexts[i];
-			for (let j = 1; j < make_mesh_layer_pass_count; ++j) {
+			for (let j: i32 = 1; j < make_mesh_layer_pass_count; ++j) {
 				if (c.name == "mesh" + j) {
 					array_remove(m.contexts, c);
 					array_remove(m._.contexts, c);
@@ -71,7 +73,7 @@ function make_material_parse_mesh_material() {
 	array_push(m._.shader.contexts, scon);
 	array_push(m._.shader._.contexts, scon);
 
-	for (let i = 1; i < make_mesh_layer_pass_count; ++i) {
+	for (let i: i32 = 1; i < make_mesh_layer_pass_count; ++i) {
 		let con = make_mesh_run({ name: "Material", canvas: null }, i);
 		let scon: shader_context_t = shader_context_create(con.data);
 		scon._.override_context = {};
@@ -100,7 +102,8 @@ function make_material_parse_mesh_material() {
 function make_material_parse_particle_material() {
 	let m = context_raw.particle_material;
 	let sc: shader_context_t = null;
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c = m._.shader._.contexts[i];
 		if (c.name == "mesh") {
 			sc = c;
 			break;
@@ -126,7 +129,8 @@ function make_material_parse_mesh_preview_material() {
 
 	let m = project_materials[0].data;
 	let scon: shader_context_t = null;
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts; ++i) {
+		let c = m._.shader._.contexts[i];
 		if (c.name == "mesh") {
 			scon = c;
 			break;
@@ -140,7 +144,7 @@ function make_material_parse_mesh_preview_material() {
 	let sd: material_t = { name: "Material", canvas: null };
 	let con = make_mesh_preview_run(sd, mcon);
 
-	for (let i = 0; i < m.contexts.length; ++i) {
+	for (let i: i32 = 0; i < m.contexts.length; ++i) {
 		if (m.contexts[i].name == "mesh") {
 			m.contexts[i] = material_context_create(mcon);
 			break;
@@ -170,7 +174,8 @@ function make_material_make_voxel(m: material_data_t) {
 	let rebuild = make_material_height_used;
 	if (config_raw.rp_gi != false && rebuild) {
 		let scon: shader_context_t = null;
-		for (let c of m._.shader._.contexts) {
+		for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+			let c = m._.shader._.contexts[i];
 			if (c.name == "voxel") {
 				scon = c;
 				break;
@@ -199,7 +204,8 @@ function make_material_parse_paint_material(bake_previews: bool = true) {
 	let m = project_materials[0].data;
 	let scon: shader_context_t = null;
 	let mcon: material_context_t = null;
-	for (let c of m._.shader._.contexts) {
+	for (let i: i32 = 0; i < m._.shader._.contexts.length; ++i) {
+		let c = m._.shader._.contexts[i];
 		if (c.name == "paint") {
 			array_remove(m._.shader.contexts, c);
 			array_remove(m._.shader._.contexts, c);
@@ -209,7 +215,8 @@ function make_material_parse_paint_material(bake_previews: bool = true) {
 			break;
 		}
 	}
-	for (let c of m.contexts) {
+	for (let i: i32 = 0; i < m.contexts.length; ++i) {
+		let c = m.contexts[i];
 		if (c.name == "paint") {
 			array_remove(m.contexts, c);
 			array_remove(m._.contexts, c);
@@ -251,9 +258,14 @@ function make_material_parse_paint_material(bake_previews: bool = true) {
 
 function make_material_bake_node_previews() {
 	context_raw.node_previews_used = [];
-	if (context_raw.node_previews == null) context_raw.node_previews = map_create();
+	if (context_raw.node_previews == null) {
+		context_raw.node_previews = map_create();
+	}
 	make_material_traverse_nodes(ui_nodes_get_canvas_material().nodes, null, []);
-	for (let key of context_raw.node_previews.keys()) {
+
+	let keys: string[] = map_keys_to_array(context_raw.node_previews);
+	for (let i: i32 = 0; i < keys.length; ++i) {
+		let key: string = keys[i];
 		if (array_index_of(context_raw.node_previews_used, key) == -1) {
 			let image = map_get(context_raw.node_previews, key);
 			base_notify_on_next_frame(function() {
@@ -265,10 +277,12 @@ function make_material_bake_node_previews() {
 }
 
 function make_material_traverse_nodes(nodes: zui_node_t[], group: zui_node_canvas_t, parents: zui_node_t[]) {
-	for (let node of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let node = nodes[i];
 		make_material_bake_node_preview(node, group, parents);
 		if (node.type == "GROUP") {
-			for (let g of project_material_groups) {
+			for (let j: i32 = 0; j < project_material_groups.length; ++j) {
+				let g = project_material_groups[j];
 				if (g.canvas.name == node.name) {
 					array_push(parents, node);
 					make_material_traverse_nodes(g.canvas.nodes, g.canvas, parents);

+ 18 - 14
armorsculpt/Sources/make_mesh.ts

@@ -112,8 +112,9 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 	// Get layers for this pass
 	make_mesh_layer_pass_count = 1;
 	let layers: SlotLayerRaw[] = [];
-	let startCount = texture_count;
-	for (let l of project_layers) {
+	let start_count = texture_count;
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l = project_layers[i];
 		if (!slot_layer_is_layer(l) || !slot_layer_is_visible(l)) {
 			continue;
 		}
@@ -125,7 +126,7 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 		}
 		texture_count += count;
 		if (texture_count >= make_mesh_get_max_textures()) {
-			texture_count = startCount + count + 3; // gbuffer0_copy, gbuffer1_copy, gbuffer2_copy
+			texture_count = start_count + count + 3; // gbuffer0_copy, gbuffer1_copy, gbuffer2_copy
 			make_mesh_layer_pass_count++;
 		}
 		if (layer_pass == make_mesh_layer_pass_count - 1) {
@@ -133,15 +134,16 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 		}
 	}
 
-	let lastPass = layer_pass == make_mesh_layer_pass_count - 1;
+	let last_pass = layer_pass == make_mesh_layer_pass_count - 1;
 
-	for (let l of layers) {
+	for (let i: i32 = 0; i < layers.length; ++i) {
+		let l = layers[i];
 		if (slot_layer_get_object_mask(l) > 0) {
 			node_shader_add_uniform(frag, 'int uid', '_uid');
 			if (slot_layer_get_object_mask(l) > project_paint_objects.length) { // Atlas
 				let visibles = project_get_atlas_objects(slot_layer_get_object_mask(l));
 				node_shader_write(frag, 'if (');
-				for (let i = 0; i < visibles.length; ++i) {
+				for (let i: i32 = 0; i < visibles.length; ++i) {
 					if (i > 0) {
 						node_shader_write(frag, ' || ');
 					}
@@ -161,17 +163,19 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 
 		let masks = slot_layer_get_masks(l);
 		if (masks != null) {
-			let hasVisible = false;
-			for (let m of masks) {
+			let has_visible = false;
+			for (let i: i32 = 0; i < masks.length; ++i) {
+				let m = masks[i];
 				if (slot_layer_is_visible(m)) {
-					hasVisible = true;
+					has_visible = true;
 					break;
 				}
 			}
-			if (hasVisible) {
+			if (has_visible) {
 				let texpaint_mask = 'texpaint_mask' + l.id;
 				node_shader_write(frag, `float ${texpaint_mask} = 0.0;`);
-				for (let m of masks) {
+				for (let i: i32 = 0; i < masks.length; ++i) {
+					let m = masks[i];
 					if (!slot_layer_is_visible(m)) continue;
 					node_shader_add_shared_sampler(frag, 'sampler2D texpaint' + m.id);
 					node_shader_write(frag, '{'); // Group mask is sampled across multiple layers
@@ -194,7 +198,7 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 			node_shader_write(frag, '}');
 		}
 
-		if (lastPass && context_raw.draw_texels) {
+		if (last_pass && context_raw.draw_texels) {
 			node_shader_add_uniform(frag, 'vec2 texpaintSize', '_texpaintSize');
 			node_shader_write(frag, 'vec2 texel0 = texCoord * texpaintSize * 0.01;');
 			node_shader_write(frag, 'vec2 texel1 = texCoord * texpaintSize * 0.1;');
@@ -204,7 +208,7 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 			node_shader_write(frag, 'basecol *= max(float(mod(int(texel2.x), 2.0) == mod(int(texel2.y), 2.0)), 0.9);');
 		}
 
-		if (lastPass && context_raw.draw_wireframe) {
+		if (last_pass && context_raw.draw_wireframe) {
 			node_shader_write(frag, 'basecol *= 1.0 - textureLod(texuvmap, texCoord, 0.0).r;');
 		}
 
@@ -219,7 +223,7 @@ function make_mesh_run(data: material_t, layer_pass: i32 = 0): NodeShaderContext
 			node_shader_write(frag, '}');
 		}
 
-		if (!lastPass) {
+		if (!last_pass) {
 			node_shader_write(frag, 'fragColor[0] = vec4(basecol, opacity);');
 			node_shader_write(frag, 'fragColor[1] = vec4(ntex, matid);');
 			node_shader_write(frag, 'fragColor[2] = vec4(occlusion, roughness, metallic, height);');

+ 24 - 21
armorsculpt/Sources/tab_layers.ts

@@ -48,7 +48,7 @@ function tab_layers_draw_full(htab: zui_handle_t) {
 }
 
 function tab_layers_draw_slots(mini: bool) {
-	for (let i = 0; i < project_layers.length; ++i) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
 		if (i >= project_layers.length) {
 			break; // Layer was deleted
 		}
@@ -61,8 +61,8 @@ function tab_layers_draw_slots(mini: bool) {
 function tab_layers_highlight_odd_lines() {
 	let ui = ui_base_ui;
 	let step = ui.t.ELEMENT_H * 2;
-	let fullH = ui._window_h - ui_base_hwnds[0].scroll_offset;
-	for (let i = 0; i < math_floor(fullH / step); ++i) {
+	let full_h = ui._window_h - ui_base_hwnds[0].scroll_offset;
+	for (let i: i32 = 0; i < math_floor(full_h / step); ++i) {
 		if (i % 2 == 0) {
 			zui_fill(0, i * step, (ui._w / zui_SCALE(ui) - 2), step, ui.t.WINDOW_BG_COL - 0x00040404);
 		}
@@ -90,41 +90,44 @@ function tab_layers_combo_filter() {
 	context_raw.layer_filter = zui_combo(filter_handle, ar, tr("Filter"), false, zui_align_t.LEFT);
 }
 
-function tab_layers_remap_layer_pointers(nodes: zui_node_t[], pointerMap: map_t<i32, i32>) {
-	for (let n of nodes) {
+function tab_layers_remap_layer_pointers(nodes: zui_node_t[], pointer_map: map_t<i32, i32>) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let n = nodes[i];
 		if (n.type == "LAYER" || n.type == "LAYER_MASK") {
 			let i = n.buttons[0].default_value;
-			if (pointerMap.has(i)) {
-				n.buttons[0].default_value = map_get(pointerMap, i);
+			if (pointer_map.has(i)) {
+				n.buttons[0].default_value = map_get(pointer_map, i);
 			}
 		}
 	}
 }
 
-function tab_layers_init_layer_map(): map_t<SlotLayerRaw, i32> {
-	let res: map_t<SlotLayerRaw, i32> = map_create();
-	for (let i = 0; i < project_layers.length; ++i) {
+function tab_layers_init_layer_map(): map_t<slot_layer_t, i32> {
+	let res: map_t<slot_layer_t, i32> = map_create();
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
 		map_set(res, project_layers[i], i);
 	}
 	return res;
 }
 
-function tab_layers_fill_layer_map(map: map_t<SlotLayerRaw, i32>): map_t<i32, i32> {
+function tab_layers_fill_layer_map(map: map_t<slot_layer_t, i32>): map_t<i32, i32> {
 	let res: map_t<i32, i32> = map_create();
-	for (let l of map.keys()) {
+	let keys: string[] = map_keys_to_array(map);
+	for (let i: i32 = 0; i < keys.length; ++i) {
+		let l = keys[i];
 		map_set(res, map_get(map, l), array_index_of(project_layers, l) > -1 ? array_index_of(project_layers, l) : 9999);
 	}
 	return res;
 }
 
-function tab_layers_set_drag_layer(layer: SlotLayerRaw, off_x: f32, off_y: f32) {
+function tab_layers_set_drag_layer(layer: slot_layer_t, off_x: f32, off_y: f32) {
 	base_drag_off_x = off_x;
 	base_drag_off_y = off_y;
 	base_drag_layer = layer;
 	context_raw.drag_dest = array_index_of(project_layers, layer);
 }
 
-function tab_layers_draw_layer_slot(l: SlotLayerRaw, i: i32, mini: bool) {
+function tab_layers_draw_layer_slot(l: slot_layer_t, i: i32, mini: bool) {
 	let ui = ui_base_ui;
 
 	if (context_raw.layer_filter > 0 &&
@@ -191,7 +194,7 @@ function tab_layers_draw_layer_slot(l: SlotLayerRaw, i: i32, mini: bool) {
 	}
 }
 
-function tab_layers_draw_layer_slot_mini(l: SlotLayerRaw, i: i32) {
+function tab_layers_draw_layer_slot_mini(l: slot_layer_t, i: i32) {
 	let ui = ui_base_ui;
 
 	zui_row([1, 1]);
@@ -204,7 +207,7 @@ function tab_layers_draw_layer_slot_mini(l: SlotLayerRaw, i: i32) {
 	ui._y -= zui_ELEMENT_OFFSET(ui);
 }
 
-function tab_layers_draw_layer_slot_full(l: SlotLayerRaw, i: i32) {
+function tab_layers_draw_layer_slot_full(l: slot_layer_t, i: i32) {
 	let ui = ui_base_ui;
 
 	let step = ui.t.ELEMENT_H;
@@ -347,7 +350,7 @@ function tab_layers_draw_layer_slot_full(l: SlotLayerRaw, i: i32) {
 	ui._y -= zui_ELEMENT_OFFSET(ui);
 }
 
-function tab_layers_combo_object(ui: zui_t, l: SlotLayerRaw, label: bool = false): zui_handle_t {
+function tab_layers_combo_object(ui: zui_t, l: slot_layer_t, label: bool = false): zui_handle_t {
 	let ar = [tr("Shared")];
 	let objectHandle = zui_nest(zui_handle("tablayers_2"), l.id);
 	objectHandle.position = l.object_mask;
@@ -355,13 +358,13 @@ function tab_layers_combo_object(ui: zui_t, l: SlotLayerRaw, label: bool = false
 	return objectHandle;
 }
 
-function tab_layers_layer_toggle_visible(l: SlotLayerRaw) {
+function tab_layers_layer_toggle_visible(l: slot_layer_t) {
 	l.visible = !l.visible;
 	ui_view2d_hwnd.redraws = 2;
 	make_material_parse_mesh_material();
 }
 
-function tab_layers_draw_layer_highlight(l: SlotLayerRaw, mini: bool) {
+function tab_layers_draw_layer_highlight(l: slot_layer_t, mini: bool) {
 	let ui = ui_base_ui;
 	let step = ui.t.ELEMENT_H;
 
@@ -379,7 +382,7 @@ function tab_layers_draw_layer_highlight(l: SlotLayerRaw, mini: bool) {
 	}
 }
 
-function tab_layers_can_merge_down(l: SlotLayerRaw) : bool {
+function tab_layers_can_merge_down(l: slot_layer_t) : bool {
 	let index = array_index_of(project_layers, l);
 	// Lowest layer
 	if (index == 0) {
@@ -400,7 +403,7 @@ function tab_layers_can_merge_down(l: SlotLayerRaw) : bool {
 	return true;
 }
 
-function tab_layers_draw_layer_context_menu(l: SlotLayerRaw, mini: bool) {
+function tab_layers_draw_layer_context_menu(l: slot_layer_t, mini: bool) {
 
 }
 

+ 2 - 1
base/Sources/args.ts

@@ -138,7 +138,8 @@ function args_run() {
 						}
 
 						let file: string = "export_presets/" + box_export_files[0] + ".json";
-						for (let f of box_export_files) {
+						for (let i: i32 = 0; i < box_export_files.length; ++i) {
+							let f: string = box_export_files[i];
 							if (f == args_export_textures_preset) {
 								file = "export_presets/" + box_export_files[array_index_of(box_export_files, f)] + ".json";
 							}

+ 52 - 25
base/Sources/base.ts

@@ -242,7 +242,8 @@ function base_init() {
 
 	// Init plugins
 	if (config_raw.plugins != null) {
-		for (let plugin of config_raw.plugins) {
+		for (let i: i32 = 0; i < config_raw.plugins.length; ++i) {
+			let plugin: string = config_raw.plugins[i];
 			plugin_start(plugin);
 		}
 	}
@@ -881,14 +882,16 @@ function base_enum_texts(node_type: string): string[] {
 	}
 	if (node_type == "LAYER" || node_type == "LAYER_MASK") {
 		let layer_names: string[] = [];
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			array_push(layer_names, l.name);
 		}
 		return layer_names;
 	}
 	if (node_type == "MATERIAL") {
 		let material_names: string[] = [];
-		for (let m of project_materials) {
+		for (let i: i32 = 0; i < project_materials.length; ++i) {
+			let m: slot_material_t = project_materials[i];
 			array_push(material_names, m.canvas.name);
 		}
 		return material_names;
@@ -941,7 +944,8 @@ function base_toggle_fullscreen() {
 }
 
 function base_is_scrolling(): bool {
-	for (let ui of base_get_uis()) {
+	for (let i: i32 = 0; i < base_get_uis().length; ++i) {
+		let ui: zui_t = base_get_uis()[i];
 		if (ui.is_scrolling) {
 			return true;
 		}
@@ -950,7 +954,8 @@ function base_is_scrolling(): bool {
 }
 
 function base_is_combo_selected(): bool {
-	for (let ui of base_get_uis()) {
+	for (let i: i32 = 0; i < base_get_uis().length; ++i) {
+		let ui: zui_t = base_get_uis()[i];
 		if (ui.combo_selected_handle_ptr != 0) {
 			return true;
 		}
@@ -1145,8 +1150,14 @@ function base_resize_layers() {
 			});
 		}
 	}
-	for (let l of project_layers) slot_layer_resize_and_set_bits(l);
-	for (let l of history_undo_layers) slot_layer_resize_and_set_bits(l);
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
+		slot_layer_resize_and_set_bits(l);
+	}
+	for (let i: i32 = 0; i < history_undo_layers.length; ++i) {
+		let l: slot_layer_t = history_undo_layers[i];
+		slot_layer_resize_and_set_bits(l);
+	}
 	let rts: map_t<string, render_target_t> = render_path_render_targets;
 	let _texpaint_blend0: image_t = map_get(rts, "texpaint_blend0")._image;
 	base_notify_on_next_frame(function() {
@@ -1182,10 +1193,12 @@ function base_resize_layers() {
 }
 
 function base_set_layer_bits() {
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		slot_layer_resize_and_set_bits(l);
 	}
-	for (let l of history_undo_layers) {
+	for (let i: i32 = 0; i < history_undo_layers.length; ++i) {
+		let l: slot_layer_t = history_undo_layers[i];
 		slot_layer_resize_and_set_bits(l);
 	}
 }
@@ -1574,7 +1587,8 @@ function base_duplicate_layer(l: slot_layer_t) {
 		context_set_layer(new_layer);
 		let masks: slot_layer_t[] = slot_layer_get_masks(l, false);
 		if (masks != null) {
-			for (let m of masks) {
+			for (let i: i32 = 0; i < masks.length; ++i) {
+				let m: slot_layer_t = masks[i];
 				m = slot_layer_duplicate(m);
 				m.parent = new_layer;
 				array_remove(project_layers, m);
@@ -1588,14 +1602,16 @@ function base_duplicate_layer(l: slot_layer_t) {
 		array_remove(project_layers, new_group);
 		array_insert(project_layers, array_index_of(project_layers, l) + 1, new_group);
 		// group.show_panel = true;
-		for (let c of slot_layer_get_children(l)) {
+		for (let i: i32 = 0; i < slot_layer_get_children(l).length; ++i) {
+			let c: slot_layer_t = slot_layer_get_children(l)[i];
 			let masks: slot_layer_t[] = slot_layer_get_masks(c, false);
 			let new_layer: slot_layer_t = slot_layer_duplicate(c);
 			new_layer.parent = new_group;
 			array_remove(project_layers, new_layer);
 			array_insert(project_layers, array_index_of(project_layers, new_group), new_layer);
 			if (masks != null) {
-				for (let m of masks) {
+				for (let i: i32 = 0; i < masks.length; ++i) {
+					let m: slot_layer_t = masks[i];
 					let new_mask: slot_layer_t = slot_layer_duplicate(m);
 					new_mask.parent = new_layer;
 					array_remove(project_layers, new_mask);
@@ -1605,7 +1621,8 @@ function base_duplicate_layer(l: slot_layer_t) {
 		}
 		let group_masks: slot_layer_t[] = slot_layer_get_masks(l);
 		if (group_masks != null) {
-			for (let m of group_masks) {
+			for (let i: i32 = 0; i < group_masks.length; ++i) {
+				let m: slot_layer_t = group_masks[i];
 				let new_mask: slot_layer_t = slot_layer_duplicate(m);
 				new_mask.parent = new_group;
 				array_remove(project_layers, new_mask);
@@ -1824,7 +1841,8 @@ function base_flatten(height_to_normal: bool = false, layers: slot_layer_t[] = n
 	g4_end();
 
 	// Flatten layers
-	for (let l1 of layers) {
+	for (let i: i32 = 0; i < layers.length; ++i) {
+		let l1: slot_layer_t = layers[i];
 		if (!slot_layer_is_visible(l1)) {
 			continue;
 		}
@@ -2011,7 +2029,8 @@ function base_is_fill_material(): bool {
 	///end
 
 	let m: slot_material_t = context_raw.material;
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (l.fill_layer == m) {
 			return true;
 		}
@@ -2054,12 +2073,14 @@ function base_update_fill_layers() {
 
 	let has_fill_layer: bool = false;
 	let has_fill_mask: bool = false;
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (slot_layer_is_layer(l) && l.fill_layer == context_raw.material) {
 			has_fill_layer = true;
 		}
 	}
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (slot_layer_is_mask(l) && l.fill_layer == context_raw.material) {
 			has_fill_mask = true;
 		}
@@ -2076,7 +2097,8 @@ function base_update_fill_layers() {
 
 		if (has_fill_layer) {
 			let first: bool = true;
-			for (let l of project_layers) {
+			for (let i: i32 = 0; i < project_layers.length; ++i) {
+				let l: slot_layer_t = project_layers[i];
 				if (slot_layer_is_layer(l) && l.fill_layer == context_raw.material) {
 					context_raw.layer = l;
 					if (first) {
@@ -2092,7 +2114,8 @@ function base_update_fill_layers() {
 		}
 		if (has_fill_mask) {
 			let first: bool = true;
-			for (let l of project_layers) {
+			for (let i: i32 = 0; i < project_layers.length; ++i) {
+				let l: slot_layer_t = project_layers[i];
 				if (slot_layer_is_mask(l) && l.fill_layer == context_raw.material) {
 					context_raw.layer = l;
 					if (first) {
@@ -2151,7 +2174,8 @@ function base_set_object_mask() {
 	///end
 
 	let ar: string[] = [tr("None")];
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		array_push(ar, p.base.name);
 	}
 
@@ -2164,7 +2188,8 @@ function base_set_object_mask() {
 			context_raw.merged_object.base.visible = false;
 		}
 		let o: mesh_object_t = project_paint_objects[0];
-		for (let p of project_paint_objects) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+			let p: mesh_object_t = project_paint_objects[i];
 			if (p.base.name == ar[mask]) {
 				o = p;
 				break;
@@ -2282,7 +2307,8 @@ function base_on_layers_resized() {
 		base_resize_layers();
 		let _layer: slot_layer_t = context_raw.layer;
 		let _material: slot_material_t = context_raw.material;
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (l.fill_layer != null) {
 				context_raw.layer = l;
 				context_raw.material = l.fill_layer;
@@ -2341,11 +2367,12 @@ function base_on_layers_resized() {
 		InpaintNode.init();
 	}
 
-	if (PhotoToPBRNode.images != null) {
-		for (let image of PhotoToPBRNode.images) {
+	if (photo_to_pbr_node_images != null) {
+		for (let i: i32 = 0; i < photo_to_pbr_node_images.length; ++i) {
+			let image: image_t = photo_to_pbr_node_images[i];
 			image_unload(image);
 		}
-		PhotoToPBRNode.images = null;
+		photo_to_pbr_node_images = null;
 		PhotoToPBRNode.init();
 	}
 

+ 6 - 3
base/Sources/box_export.ts

@@ -342,7 +342,8 @@ function box_export_tab_export_mesh(ui: zui_t, htab: zui_handle_t) {
 		context_raw.export_mesh_format = zui_combo(zui_handle("boxexport_9", { position: context_raw.export_mesh_format }), ["obj", "arm"], tr("Format"), true);
 
 		let ar: string[] = [tr("All")];
-		for (let p of project_paint_objects) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+			let p: mesh_object_t = project_paint_objects[i];
 			array_push(ar, p.base.name);
 		}
 		zui_combo(box_export_mesh_handle, ar, tr("Meshes"), true);
@@ -352,8 +353,10 @@ function box_export_tab_export_mesh(ui: zui_t, htab: zui_handle_t) {
 		let tris: i32 = 0;
 		let pos: i32 = box_export_mesh_handle.position;
 		let paint_objects: mesh_object_t[] = pos == 0 ? project_paint_objects : [project_paint_objects[pos - 1]];
-		for (let po of paint_objects) {
-			for (let inda of po.data.index_arrays) {
+		for (let i: i32 = 0; i < paint_objects.length; ++i) {
+			let po: mesh_object_t = paint_objects[i];
+			for (let i: i32 = 0; i < po.data.index_arrays.length; ++i) {
+				let inda: index_array_t = po.data.index_arrays[i];
 				tris += math_floor(inda.values.length / 3);
 			}
 		}

+ 23 - 18
base/Sources/box_preferences.ts

@@ -93,7 +93,8 @@ function box_preferences_show() {
 							config_restore();
 							box_preferences_set_scale();
 							if (box_preferences_files_plugin != null) {
-								for (let f of box_preferences_files_plugin) {
+								for (let i: i32 = 0; i < box_preferences_files_plugin.length; ++i) {
+									let f: string = box_preferences_files_plugin[i];
 									plugin_stop(f);
 								}
 							}
@@ -208,7 +209,7 @@ function box_preferences_show() {
 			}
 			h.text = val.toString(16);
 			zui_text_input(h, "VIEWPORT_COL");
-			h.color = parseInt(h.text, 16);
+			h.color = parse_int_hex(h.text);
 
 			if (box_preferences_world_color != h.color) {
 				box_preferences_world_color = h.color;
@@ -225,7 +226,9 @@ function box_preferences_show() {
 			}
 
 			// Theme fields
-			for (let key of Object.getOwnPropertyNames(theme_t.prototype)) {
+			let props: string[] = Object.getOwnPropertyNames(theme_t.prototype);
+			for (let i: i32 = 0; i < props.length; ++i) {
+				let key: string = props[i];
 				if (key == "constructor") {
 					continue;
 				}
@@ -233,12 +236,12 @@ function box_preferences_show() {
 				let h: zui_handle_t = zui_nest(hlist, i++);
 				let val: any = theme[key];
 
-				let isHex: bool = ends_with(key, "_COL");
-				if (isHex && val < 0) {
+				let is_hex: bool = ends_with(key, "_COL");
+				if (is_hex && val < 0) {
 					val += 4294967296;
 				}
 
-				if (isHex) {
+				if (is_hex) {
 					zui_row([1 / 8, 7 / 8]);
 					zui_text("", 0, val);
 					if (ui.is_hovered && ui.input_released) {
@@ -268,18 +271,19 @@ function box_preferences_show() {
 					theme[key] = i;
 				}
 				else {
-					h.text = isHex ? val.toString(16) : val.toString();
+					h.text = is_hex ? val.toString(16) : val.toString();
 					zui_text_input(h, key);
-					if (isHex) {
-						theme[key] = parseInt(h.text, 16);
+					if (is_hex) {
+						theme[key] = parse_int_hex(h.text);
 					}
 					else {
-						theme[key] = parseInt(h.text);
+						theme[key] = parse_int(h.text);
 					}
 				}
 
 				if (ui.changed) {
-					for (let ui of base_get_uis()) {
+					for (let i: i32 = 0; i < base_get_uis().length; ++i) {
+						let ui: zui_t = base_get_uis()[i];
 						ui.elements_baked = false;
 					}
 				}
@@ -629,14 +633,14 @@ function box_preferences_show() {
 						if (zui_button(tr("OK")) || ui.is_return_down) {
 							let template: string =
 `let plugin = create();
-let h1 = new Handle();
-plugin.drawUI = function (ui) {
-if (Zui.panel(h1, 'New Plugin')) {
-	if (Zui.button('Button')) {
-		console.error('Hello');
+let h1 = zui_handle_create();
+plugin.draw_ui = function (ui) {
+	if (zui_panel(h1, 'New Plugin')) {
+		if (zui_button('Button')) {
+			console.error('Hello');
+		}
 	}
 }
-}
 `;
 							if (!ends_with(plugin_name, ".js")) {
 								plugin_name += ".js";
@@ -666,7 +670,8 @@ if (Zui.panel(h1, 'New Plugin')) {
 				config_raw.plugins = [];
 			}
 			let h: zui_handle_t = zui_handle("boxpreferences_56", { selected: false });
-			for (let f of box_preferences_files_plugin) {
+			for (let i: i32 = 0; i < box_preferences_files_plugin.length; ++i) {
+				let f: string = box_preferences_files_plugin[i];
 				let is_js: bool = ends_with(f, ".js");
 				if (!is_js) {
 					continue;

+ 5 - 2
base/Sources/box_projects.ts

@@ -5,7 +5,9 @@ let box_projects_icon_map: map_t<string, image_t> = null;
 
 function box_projects_show() {
 	if (box_projects_icon_map != null) {
-		for (let handle of box_projects_icon_map.keys()) {
+		let keys: string[] = map_keys_to_array(box_projects_icon_map);
+		for (let i: i32 = 0; i < keys.length; ++i) {
+			let handle: string = keys[i];
 			data_delete_image(handle);
 		}
 		box_projects_icon_map = null;
@@ -190,7 +192,8 @@ function box_projects_recent_tab(ui: zui_t) {
 		box_projects_hsearch.text = zui_text_input(box_projects_hsearch, tr("Search"), zui_align_t.LEFT, true, true);
 		ui.enabled = true;
 
-		for (let path of config_raw.recent_projects) {
+		for (let i: i32 = 0; i < config_raw.recent_projects.length; ++i) {
+			let path: string = config_raw.recent_projects[i];
 			let file: string = path;
 			///if krom_windows
 			file = string_replace_all(path, "/", "\\");

+ 13 - 22
base/Sources/config.ts

@@ -7,30 +7,20 @@ let config_default_button_spacing: string = "       ";
 let config_button_spacing: string = config_default_button_spacing;
 
 function config_load(done: ()=>void) {
-	try {
-		let blob: buffer_t = data_get_blob((path_is_protected() ? krom_save_path() : "") + "config.json");
+	let blob: buffer_t = data_get_blob((path_is_protected() ? krom_save_path() : "") + "config.json");
+
+	///if krom_linux
+	if (blob == null) { // Protected directory
+		blob = data_get_blob(krom_save_path() + "config.json");
+	}
+	///end
+
+	if (blob != null) {
 		config_loaded = true;
 		config_raw = json_parse(sys_buffer_to_string(blob));
-		done();
-	}
-	catch (e: any) {
-		krom_log(e);
-
-		///if krom_linux
-		try { // Protected directory
-			let blob: buffer_t = data_get_blob(krom_save_path() + "config.json");
-			config_loaded = true;
-			config_raw = json_parse(sys_buffer_to_string(blob));
-			done();
-		}
-		catch (e: any) {
-			krom_log(e);
-			done();
-		}
-		///else
-		done();
-		///end
 	}
+
+	done();
 }
 
 function config_save() {
@@ -287,7 +277,8 @@ function config_load_theme(theme: string, tagRedraw: bool = true) {
 	}
 	base_theme.FILL_WINDOW_BG = true;
 	if (tagRedraw) {
-		for (let ui of base_get_uis()) {
+		for (let i: i32 = 0; i < base_get_uis().length; ++i) {
+			let ui: zui_t = base_get_uis()[i];
 			ui.t = base_theme;
 		}
 		ui_base_tag_ui_redraw();

+ 7 - 4
base/Sources/context.ts

@@ -175,7 +175,7 @@ type context_t = {
 	last_particle_hit_y?: f32;
 	last_particle_hit_z?: f32;
 	particle_timer?: tween_anim_t;
-	paint_body?: PhysicsBodyRaw;
+	paint_body?: physics_body_t;
 	///end
 
 	layer_filter?: i32;
@@ -686,7 +686,8 @@ function context_init_tool() {
 function context_select_paint_object(o: mesh_object_t) {
 	///if (is_paint || is_sculpt)
 	ui_header_handle.redraws = 2;
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		p.skip_context = "paint";
 	}
 	context_raw.paint_object = o;
@@ -711,7 +712,8 @@ function context_select_paint_object(o: mesh_object_t) {
 
 function context_main_object(): mesh_object_t {
 	///if (is_paint || is_sculpt)
-	for (let po of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let po: mesh_object_t = project_paint_objects[i];
 		if (po.base.children.length > 0) {
 			return po;
 		}
@@ -880,7 +882,8 @@ function context_enable_import_plugin(file: string): bool {
 		box_preferences_fetch_plugins();
 	}
 	let ext: string = substring(file, string_last_index_of(file, ".") + 1, file.length);
-	for (let f of box_preferences_files_plugin) {
+	for (let i: i32 = 0; i < box_preferences_files_plugin.length; ++i) {
+		let f: string = box_preferences_files_plugin[i];
 		if (starts_with(f, "import_") && string_index_of(f, ext) >= 0) {
 			config_enable_plugin(f);
 			console_info(f + " " + tr("plugin enabled"));

+ 75 - 23
base/Sources/export_arm.ts

@@ -1,44 +1,66 @@
 
 function export_arm_run_mesh(path: string, paint_objects: mesh_object_t[]) {
 	let mesh_datas: mesh_data_t[] = [];
-	for (let p of paint_objects) array_push(mesh_datas, p.data);
+	for (let i: i32 = 0; i < paint_objects.length; ++i) {
+		let p: mesh_object_t = paint_objects[i];
+		array_push(mesh_datas, p.data);
+	}
 	let raw: scene_t = { mesh_datas: mesh_datas };
 	let b: buffer_t = armpack_encode(raw);
-	if (!ends_with(path, ".arm")) path += ".arm";
+	if (!ends_with(path, ".arm")) {
+		path += ".arm";
+	}
 	krom_file_save_bytes(path, b, buffer_size(b) + 1);
 }
 
 function export_arm_run_project() {
 	///if (is_paint || is_sculpt)
 	let mnodes: zui_node_canvas_t[] = [];
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		let c: zui_node_canvas_t = json_parse(json_stringify(m.canvas));
-		for (let n of c.nodes) export_arm_export_node(n);
+		for (let i: i32 = 0; i < c.nodes.length; ++i) {
+			let n: zui_node_t = c.nodes[i];
+			export_arm_export_node(n);
+		}
 		array_push(mnodes, c);
 	}
 
 	let bnodes: zui_node_canvas_t[] = [];
-	for (let b of project_brushes) array_push(bnodes, b.canvas);
+	for (let i: i32 = 0; i < project_brushes.length; ++i) {
+		let b: slot_brush_t = project_brushes[i];
+		array_push(bnodes, b.canvas);
+	}
 	///end
 
 	///if is_lab
 	let c: zui_node_canvas_t = json_parse(json_stringify(project_canvas));
-	for (let n of c.nodes) export_arm_export_node(n);
+	for (let i: i32 = 0; i < c.nodes.length; ++i) {
+		let n: zui_node_t = c.nodes[i];
+		export_arm_export_node(n);
+	}
 	///end
 
 	let mgroups: zui_node_canvas_t[] = null;
 	if (project_material_groups.length > 0) {
 		mgroups = [];
-		for (let g of project_material_groups) {
+		for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+			let g: node_group_t = project_material_groups[i];
 			let c: zui_node_canvas_t = json_parse(json_stringify(g.canvas));
-			for (let n of c.nodes) export_arm_export_node(n);
+			for (let i: i32 = 0; i < c.nodes.length; ++i) {
+				let n: zui_node_t = c.nodes[i];
+				export_arm_export_node(n);
+			}
 			array_push(mgroups, c);
 		}
 	}
 
 	///if (is_paint || is_sculpt)
 	let md: mesh_data_t[] = [];
-	for (let p of project_paint_objects) array_push(md, p.data);
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
+		array_push(md, p.data);
+	}
 	///end
 
 	///if is_lab
@@ -55,7 +77,8 @@ function export_arm_run_project() {
 	let bpp: i32 = bits_pos == texture_bits_t.BITS8 ? 8 : bits_pos == texture_bits_t.BITS16 ? 16 : 32;
 
 	let ld: layer_data_t[] = [];
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		array_push(ld, {
 			name: l.name,
 			res: l.texpaint != null ? l.texpaint.width : project_layers[0].texpaint.width,
@@ -152,7 +175,7 @@ function export_arm_run_project() {
 	g2_end();
 	///end
 	let mesh_icon_pixels: buffer_t = image_get_pixels(mesh_icon);
-	let u8a: u8_array_t = new u8_array_t(mesh_icon_pixels);
+	let u8a: u8_array_t = u8_array_create_from_buffer(mesh_icon_pixels);
 	for (let i: i32 = 0; i < 256 * 256 * 4; ++i) {
 		u8a[i] = math_floor(math_pow(u8a[i] / 255, 1.0 / 2.2) * 255);
 	}
@@ -216,9 +239,21 @@ function export_arm_export_node(n: zui_node_t, assets: asset_t[] = null) {
 		}
 	}
 	// Pack colors
-	if (n.color > 0) n.color -= 4294967296;
-	for (let inp of n.inputs) if (inp.color > 0) inp.color -= 4294967296;
-	for (let out of n.outputs) if (out.color > 0) out.color -= 4294967296;
+	if (n.color > 0) {
+		n.color -= 4294967296;
+	}
+	for (let i: i32 = 0; i < n.inputs.length; ++i) {
+		let inp: zui_node_socket_t = n.inputs[i];
+		if (inp.color > 0) {
+			inp.color -= 4294967296;
+		}
+	}
+	for (let i: i32 = 0; i < n.outputs.length; ++i) {
+		let out: zui_node_socket_t = n.outputs[i];
+		if (out.color > 0) {
+			out.color -= 4294967296;
+		}
+	}
 }
 
 ///if (is_paint || is_sculpt)
@@ -232,14 +267,25 @@ function export_arm_run_material(path: string) {
 	if (ui_nodes_has_group(c)) {
 		mgroups = [];
 		ui_nodes_traverse_group(mgroups, c);
-		for (let gc of mgroups) for (let n of gc.nodes) export_arm_export_node(n, assets);
+		for (let i: i32 = 0; i < mgroups.length; ++i) {
+			let gc: zui_node_canvas_t = mgroups[i];
+			for (let i: i32 = 0; i < gc.nodes.length; ++i) {
+				let n: zui_node_t = gc.nodes[i];
+				export_arm_export_node(n, assets);
+			}
+		}
+	}
+	for (let i: i32 = 0; i < c.nodes.length; ++i) {
+		let n: zui_node_t = c.nodes[i];
+		export_arm_export_node(n, assets);
 	}
-	for (let n of c.nodes) export_arm_export_node(n, assets);
 	array_push(mnodes, c);
 
 	let texture_files: string[] = export_arm_assets_to_files(path, assets);
 	let is_cloud: bool = ends_with(path, "_cloud_.arm");
-	if (is_cloud) path = string_replace_all(path, "_cloud_", "");
+	if (is_cloud) {
+		path = string_replace_all(path, "_cloud_", "");
+	}
 	let packed_assets: packed_asset_t[] = null;
 	if (!context_raw.pack_assets_on_export) {
 		packed_assets = export_arm_get_packed_assets(path, texture_files);
@@ -296,7 +342,8 @@ function export_arm_run_brush(path: string) {
 	let b: slot_brush_t = context_raw.brush;
 	let c: zui_node_canvas_t = json_parse(json_stringify(b.canvas));
 	let assets: asset_t[] = [];
-	for (let n of c.nodes) {
+	for (let i: i32 = 0; i < c.nodes.length; ++i) {
+		let n: zui_node_t = c.nodes[i];
 		export_arm_export_node(n, assets);
 	}
 	array_push(bnodes, c);
@@ -339,7 +386,8 @@ function export_arm_run_brush(path: string) {
 
 function export_arm_assets_to_files(project_path: string, assets: asset_t[]): string[] {
 	let texture_files: string[] = [];
-	for (let a of assets) {
+	for (let i: i32 = 0; i < assets.length; ++i) {
+		let a: asset_t = assets[i];
 		///if krom_ios
 		let same_drive: bool = false;
 		///else
@@ -359,7 +407,8 @@ function export_arm_assets_to_files(project_path: string, assets: asset_t[]): st
 ///if (is_paint || is_sculpt)
 function export_arm_meshes_to_files(project_path: string): string[] {
 	let mesh_files: string[] = [];
-	for (let file of project_mesh_assets) {
+	for (let i: i32 = 0; i < project_mesh_assets.length; ++i) {
+		let file: string = project_mesh_assets[i];
 		///if krom_ios
 		let same_drive: bool = false;
 		///else
@@ -400,7 +449,8 @@ function export_arm_fonts_to_files(project_path: string, fonts: slot_font_t[]):
 function export_arm_get_packed_assets(project_path: string, texture_files: string[]): packed_asset_t[] {
 	let packed_assets: packed_asset_t[] = null;
 	if (project_raw.packed_assets != null) {
-		for (let pa of project_raw.packed_assets) {
+		for (let i: i32 = 0; i < project_raw.packed_assets.length; ++i) {
+			let pa: packed_asset_t = project_raw.packed_assets[i];
 			///if krom_ios
 			let same_drive: bool = false;
 			///else
@@ -408,7 +458,8 @@ function export_arm_get_packed_assets(project_path: string, texture_files: strin
 			///end
 			// Convert path from absolute to relative
 			pa.name = same_drive ? path_to_relative(project_path, pa.name) : pa.name;
-			for (let tf of texture_files) {
+			for (let i: i32 = 0; i < texture_files.length; ++i) {
+				let tf: string = texture_files[i];
 				if (pa.name == tf) {
 					if (packed_assets == null) {
 						packed_assets = [];
@@ -444,7 +495,8 @@ function export_arm_pack_assets(raw: project_format_t, assets: asset_t[]) {
 		}
 	}
 	base_notify_on_next_frame(function () {
-		for (let image of temp_images) {
+		for (let i: i32 = 0; i < temp_images.length; ++i) {
+			let image: image_t = temp_images[i];
 			image_unload(image);
 		}
 	});

+ 2 - 1
base/Sources/export_gpl.ts

@@ -6,7 +6,8 @@ function export_gpl_run(path: string, name: string, swatches: swatch_color_t[])
 	o += "# armorpaint.org\n";
 	o += "#\n";
 
-	for (let swatch of swatches) {
+	for (let i: i32 = 0; i < swatches.length; ++i) {
+		let swatch: swatch_color_t = swatches[i];
 		o += any_to_string(color_get_rb(swatch.base)) + " " + any_to_string(color_get_gb(swatch.base)) + " " + any_to_string(color_get_bb(swatch.base)) + "\n";
 	}
 

+ 2 - 1
base/Sources/export_obj.ts

@@ -12,7 +12,8 @@ function export_obj_run(path: string, paint_objects: mesh_object_t[], apply_disp
 	let poff: i32 = 0;
 	let noff: i32 = 0;
 	let toff: i32 = 0;
-	for (let p of paint_objects) {
+	for (let i: i32 = 0; i < paint_objects.length; ++i) {
+		let p: mesh_object_t = paint_objects[i];
 		let mesh: mesh_data_t = p.data;
 		let inv: f32 = 1 / 32767;
 		let sc: f32 = p.data.scale_pos * inv;

+ 18 - 9
base/Sources/export_texture.ts

@@ -11,7 +11,8 @@ function export_texture_run(path: string, bake_material: bool = false) {
 	}
 	else if (context_raw.layers_export == export_mode_t.PER_UDIM_TILE) {
 		let udim_tiles: string[] = [];
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (slot_layer_get_object_mask(l) > 0) {
 				let name: string = project_paint_objects[slot_layer_get_object_mask(l) - 1].base.name;
 				if (substring(name, name.length - 5, 2) == ".1") { // tile.1001
@@ -20,7 +21,8 @@ function export_texture_run(path: string, bake_material: bool = false) {
 			}
 		}
 		if (udim_tiles.length > 0) {
-			for (let udim_tile of udim_tiles) {
+			for (let i: i32 = 0; i < udim_tiles.length; ++i) {
+				let udim_tile: string = udim_tiles[i];
 				export_texture_run_layers(path, project_layers, udim_tile);
 			}
 		}
@@ -30,7 +32,8 @@ function export_texture_run(path: string, bake_material: bool = false) {
 	}
 	else if (context_raw.layers_export == export_mode_t.PER_OBJECT) {
 		let object_names: string[] = [];
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (slot_layer_get_object_mask(l) > 0) {
 				let name: string = project_paint_objects[slot_layer_get_object_mask(l) - 1].base.name;
 				if (array_index_of(object_names, name) == -1) {
@@ -39,7 +42,8 @@ function export_texture_run(path: string, bake_material: bool = false) {
 			}
 		}
 		if (object_names.length > 0) {
-			for (let name of object_names) {
+			for (let i: i32 = 0; i < object_names.length; ++i) {
+				let name: string = object_names[i];
 				export_texture_run_layers(path, project_layers, name);
 			}
 		}
@@ -62,7 +66,8 @@ function export_texture_run(path: string, bake_material: bool = false) {
 				let layers: slot_layer_t[] = [];
 				for (let object_index: i32 = 0; object_index < project_atlas_objects.length; ++object_index) {
 					if (project_atlas_objects[object_index] == atlas_index) {
-						for (let l of project_layers) {
+						for (let i: i32 = 0; i < project_layers.length; ++i) {
+							let l: slot_layer_t = project_layers[i];
 							if (slot_layer_get_object_mask(l) == 0 || // shared object
 								slot_layer_get_object_mask(l) - 1 == object_index) {
 								array_push(layers, l);
@@ -184,7 +189,8 @@ function export_texture_run_layers(path: string, layers: any[], object_name: str
 	g4_end();
 
 	// Flatten layers
-	for (let l1 of layers) {
+	for (let i: i32 = 0; i < layers.length; ++i) {
+		let l1: slot_layer_t = layers[i];
 		if (!export_selected && !slot_layer_is_visible(l1)) {
 			continue;
 		}
@@ -310,8 +316,10 @@ function export_texture_run_layers(path: string, layers: any[], object_name: str
 	let preset: export_preset_t = box_export_preset;
 	let pix: buffer_t = null;
 
-	for (let t of preset.textures) {
-		for (let c of t.channels) {
+	for (let i: i32 = 0; i < preset.textures.length; ++i) {
+		let t: export_preset_texture_t = preset.textures[i];
+		for (let i: i32 = 0; i < t.channels.length; ++i) {
+			let c: string = t.channels[i];
 			if ((c == "base_r" || c == "base_g" || c == "base_b" || c == "opac") && pixpaint == null) {
 				pixpaint = image_get_pixels(texpaint);
 			}
@@ -324,7 +332,8 @@ function export_texture_run_layers(path: string, layers: any[], object_name: str
 		}
 	}
 
-	for (let t of preset.textures) {
+	for (let i: i32 = 0; i < preset.textures.length; ++i) {
+		let t: export_preset_texture_t = preset.textures[i];
 		let c: string[] = t.channels;
 		let tex_name = t.name != "" ? "_" + t.name : "";
 		let single_channel: bool = c[0] == c[1] && c[1] == c[2] && c[3] == "1.0";

+ 3 - 6
base/Sources/file.ts

@@ -85,11 +85,7 @@ function file_download(url: string, dstPath: string, done: ()=>void, size: i32 =
 function file_download_bytes(url: string, done: (ab: buffer_t)=>void) {
 	let save: string = (path_is_protected() ? krom_save_path() : path_data() + path_sep) + "download.bin";
 	file_download(url, save, function() {
-		let buffer: buffer_t = null;
-		try {
-			buffer = krom_load_blob(save);
-		}
-		catch (e: any) {}
+		let buffer: buffer_t = krom_load_blob(save);
 		done(buffer);
 	});
 }
@@ -163,7 +159,8 @@ function file_init_cloud_bytes(done: ()=>void, append: string = "") {
 			array_push(sizes, Number(substring(str, pos_start, pos_end)));
 		}
 
-		for (let file of files) {
+		for (let i: i32 = 0; i < files.length; ++i) {
+			let file: string = files[i];
 			if (path_is_folder(file)) {
 				map_set(file_cloud, substring(file, 0, file.length - 1), []);
 			}

+ 4 - 2
base/Sources/history.ts

@@ -123,7 +123,8 @@ function history_undo() {
 			let layers_to_restore: slot_layer_t[] = slot_layer_is_group(current_layer) ? slot_layer_get_children(current_layer) : [current_layer];
 			layers_to_restore.reverse();
 
-			for (let layer of layers_to_restore) {
+			for (let i: i32 = 0; i < layers_to_restore.length; ++i) {
+				let layer: slot_layer_t = layers_to_restore[i];
 				// Replace the current layer's content with the old one
 				context_raw.layer = layer;
 				history_undo_i = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
@@ -664,7 +665,8 @@ function history_copy_merging_layers() {
 }
 
 function history_copy_merging_layers2(layers: slot_layer_t[]) {
-	for (let layer of layers) {
+	for (let i: i32 = 0; i < layers.length; ++i) {
+		let layer: slot_layer_t = layers[i];
 		history_copy_to_undo(layer.id, history_undo_i, slot_layer_is_mask(layer));
 	}
 }

+ 47 - 24
base/Sources/import_arm.ts

@@ -48,7 +48,7 @@ function import_arm_run_project(path: string) {
 
 	// Save to recent
 	///if krom_ios
-	let recent_path: string = substring(path, string_last_index_of(path, "/") + 1);
+	let recent_path: string = substring(path, string_last_index_of(path, "/") + 1, path.length);
 	///else
 	let recent_path: string = path;
 	///end
@@ -86,7 +86,8 @@ function import_arm_run_project(path: string) {
 		camera_origins[0].z = origin[2];
 	}
 
-	for (let file of project.assets) {
+	for (let i: i32 = 0; i < project.assets.length; ++i) {
+		let file: string = project.assets[i];
 		///if krom_windows
 		file = string_replace_all(file, "/", "\\");
 		///else
@@ -107,7 +108,8 @@ function import_arm_run_project(path: string) {
 
 	///if (is_paint || is_sculpt)
 	if (project.font_assets != null) {
-		for (let file of project.font_assets) {
+		for (let i: i32 = 0; i < project.font_assets.length; ++i) {
+			let file: string = project.font_assets[i];
 			///if krom_windows
 			file = string_replace_all(file, "/", "\\");
 			///else
@@ -176,7 +178,8 @@ function import_arm_run_project(path: string) {
 	let tex: image_t = project_layers[0].texpaint;
 	if (tex.width != config_get_texture_res_x() || tex.height != config_get_texture_res_y()) {
 		if (history_undo_layers != null) {
-			for (let l of history_undo_layers) {
+			for (let i: i32 = 0; i < history_undo_layers.length; ++i) {
+				let l: slot_layer_t = history_undo_layers[i];
 				slot_layer_resize_and_set_bits(l);
 			}
 		}
@@ -198,7 +201,8 @@ function import_arm_run_project(path: string) {
 		context_raw.brush_blend_dirty = true;
 	}
 
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		slot_layer_unload(l);
 	}
 	project_layers = [];
@@ -319,7 +323,8 @@ function import_arm_run_project(path: string) {
 	let m0: material_data_t = data_get_material("Scene", "Material");
 
 	project_materials = [];
-	for (let n of project.material_nodes) {
+	for (let i: i32 = 0; i < project.material_nodes.length; ++i) {
+		let n: zui_node_canvas_t = project.material_nodes[i];
 		import_arm_init_nodes(n.nodes);
 		context_raw.material = slot_material_create(m0, n);
 		array_push(project_materials, context_raw.material);
@@ -330,20 +335,23 @@ function import_arm_run_project(path: string) {
 	ui_nodes_group_stack = [];
 	project_material_groups = [];
 	if (project.material_groups != null) {
-		for (let g of project.material_groups) {
+		for (let i: i32 = 0; i < project.material_groups.length; ++i) {
+			let g: zui_node_canvas_t = project.material_groups[i];
 			array_push(project_material_groups, { canvas: g, nodes: zui_nodes_create() });
 		}
 	}
 
 	///if (is_paint || is_sculpt)
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		context_raw.material = m;
 		make_material_parse_paint_material();
 		util_render_make_material_preview();
 	}
 
 	project_brushes = [];
-	for (let n of project.brush_nodes) {
+	for (let i: i32 = 0; i < project.brush_nodes.length; ++i) {
+		let n: zui_node_canvas_t = project.brush_nodes[i];
 		import_arm_init_nodes(n.nodes);
 		context_raw.brush = slot_brush_create(n);
 		array_push(project_brushes, context_raw.brush);
@@ -415,7 +423,8 @@ function import_arm_run_material(path: string) {
 
 function import_arm_run_material_from_project(project: project_format_t, path: string) {
 	let base: string = path_base_dir(path);
-	for (let file of project.assets) {
+	for (let i: i32 = 0; i < project.assets.length; ++i) {
+		let file: string = project.assets[i];
 		///if krom_windows
 		file = string_replace_all(file, "/", "\\");
 		///else
@@ -437,7 +446,8 @@ function import_arm_run_material_from_project(project: project_format_t, path: s
 
 	let imported: slot_material_t[] = [];
 
-	for (let c of project.material_nodes) {
+	for (let i: i32 = 0; i < project.material_nodes.length; ++i) {
+		let c: zui_node_canvas_t = project.material_nodes[i];
 		import_arm_init_nodes(c.nodes);
 		context_raw.material = slot_material_create(m0, c);
 		array_push(project_materials, context_raw.material);
@@ -446,7 +456,8 @@ function import_arm_run_material_from_project(project: project_format_t, path: s
 	}
 
 	if (project.material_groups != null) {
-		for (let c of project.material_groups) {
+		for (let i: i32 = 0; i < project.material_groups.length; ++i) {
+			let c: zui_node_canvas_t = project.material_groups[i];
 			while (import_arm_group_exists(c)) {
 				import_arm_rename_group(c.name, imported, project.material_groups); // Ensure unique group name
 			}
@@ -456,7 +467,8 @@ function import_arm_run_material_from_project(project: project_format_t, path: s
 	}
 
 	let _init = function () {
-		for (let m of imported) {
+		for (let i: i32 = 0; i < imported.length; ++i) {
+			let m: slot_material_t = imported[i];
 			context_set_material(m);
 			make_material_parse_paint_material();
 			util_render_make_material_preview();
@@ -470,7 +482,8 @@ function import_arm_run_material_from_project(project: project_format_t, path: s
 }
 
 function import_arm_group_exists(c: zui_node_canvas_t): bool {
-	for (let g of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let g: node_group_t = project_material_groups[i];
 		if (g.canvas.name == c.name) {
 			return true;
 		}
@@ -479,18 +492,22 @@ function import_arm_group_exists(c: zui_node_canvas_t): bool {
 }
 
 function import_arm_rename_group(name: string, materials: slot_material_t[], groups: zui_node_canvas_t[]) {
-	for (let m of materials) {
-		for (let n of m.canvas.nodes) {
+	for (let i: i32 = 0; i < materials.length; ++i) {
+		let m: slot_material_t = materials[i];
+		for (let i: i32 = 0; i < m.canvas.nodes.length; ++i) {
+			let n: zui_node_t = m.canvas.nodes[i];
 			if (n.type == "GROUP" && n.name == name) {
 				n.name += ".1";
 			}
 		}
 	}
-	for (let c of groups) {
+	for (let i: i32 = 0; i < groups.length; ++i) {
+		let c: zui_node_canvas_t = groups[i];
 		if (c.name == name) {
 			c.name += ".1";
 		}
-		for (let n of c.nodes) {
+		for (let i: i32 = 0; i < c.nodes.length; ++i) {
+			let n: zui_node_t = c.nodes[i];
 			if (n.type == "GROUP" && n.name == name) {
 				n.name += ".1";
 			}
@@ -510,7 +527,8 @@ function import_arm_run_brush(path: string) {
 
 function import_arm_run_brush_from_project(project: project_format_t, path: string) {
 	let base: string = path_base_dir(path);
-	for (let file of project.assets) {
+	for (let i: i32 = 0; i < project.assets.length; ++i) {
+		let file: string = project.assets[i];
 		///if krom_windows
 		file = string_replace_all(file, "/", "\\");
 		///else
@@ -530,7 +548,8 @@ function import_arm_run_brush_from_project(project: project_format_t, path: stri
 
 	let imported: slot_brush_t[] = [];
 
-	for (let n of project.brush_nodes) {
+	for (let i: i32 = 0; i < project.brush_nodes.length; ++i) {
+		let n: zui_node_canvas_t = project.brush_nodes[i];
 		import_arm_init_nodes(n.nodes);
 		context_raw.brush = slot_brush_create(n);
 		array_push(project_brushes, context_raw.brush);
@@ -538,7 +557,8 @@ function import_arm_run_brush_from_project(project: project_format_t, path: stri
 	}
 
 	let _init = function () {
-		for (let b of imported) {
+		for (let i: i32 = 0; i < imported.length; ++i) {
+			let b: slot_brush_t = imported[i];
 			context_set_brush(b);
 			util_render_make_brush_preview();
 		}
@@ -570,7 +590,8 @@ function import_arm_run_swatches_from_project(project: project_format_t, path: s
 	}
 
 	if (project.swatches != null) {
-		for (let s of project.swatches) {
+		for (let i: i32 = 0; i < project.swatches.length; ++i) {
+			let s: swatch_color_t = project.swatches[i];
 			array_push(project_raw.swatches, s);
 		}
 	}
@@ -598,7 +619,8 @@ function import_arm_texture_node_name(): string {
 }
 
 function import_arm_init_nodes(nodes: zui_node_t[]) {
-	for (let node of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let node: zui_node_t = nodes[i];
 		if (node.type == import_arm_texture_node_name()) {
 			node.buttons[0].default_value = base_get_asset_index(node.buttons[0].data);
 			node.buttons[0].data = "";
@@ -610,7 +632,8 @@ function import_arm_unpack_asset(project: project_format_t, abs: string, file: s
 	if (project_raw.packed_assets == null) {
 		project_raw.packed_assets = [];
 	}
-	for (let pa of project.packed_assets) {
+	for (let i: i32 = 0; i < project.packed_assets.length; ++i) {
+		let pa: packed_asset_t = project.packed_assets[i];
 		///if krom_windows
 		pa.name = string_replace_all(pa.name, "/", "\\");
 		///else

+ 18 - 10
base/Sources/import_blend_material.ts

@@ -3,13 +3,13 @@
 
 function import_blend_material_run(path: string) {
 	let b: buffer_t = data_get_blob(path);
-	let bl: BlendRaw = parser_blend_init(b);
+	let bl: blend_t = parser_blend_init(b);
 	if (bl.dna == null) {
 		console_error(strings_error3());
 		return;
 	}
 
-	let mats: BlHandleRaw[] = parser_blend_get(bl, "Material");
+	let mats: bl_handle_t[] = parser_blend_get(bl, "Material");
 	if (mats.length == 0) {
 		console_error("Error: No materials found");
 		return;
@@ -17,7 +17,8 @@ function import_blend_material_run(path: string) {
 
 	let imported: slot_material_t[] = [];
 
-	for (let mat of mats) {
+	for (let i: i32 = 0; i < mats.length; ++i) {
+		let mat: bl_handle_t = mats[i];
 		// Material slot
 		context_raw.material = slot_material_create(project_materials[0].data);
 		array_push(project_materials, context_raw.material);
@@ -27,13 +28,15 @@ function import_blend_material_run(path: string) {
 		canvas.name = bl_handle_get(bl_handle_get(mat, "id"), "name"); // MAWood
 		canvas.name = substring(canvas.name, 2, canvas.name.length);
 		let nout: zui_node_t = null;
-		for (let n of canvas.nodes) {
+		for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+			let n: zui_node_t = canvas.nodes[i];
 			if (n.type == "OUTPUT_MATERIAL_PBR") {
 				nout = n;
 				break;
 			}
 		}
-		for (let n of canvas.nodes) {
+		for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+			let n: zui_node_t = canvas.nodes[i];
 			if (n.name == "RGB") {
 				zui_remove_node(n, canvas);
 				break;
@@ -74,9 +77,11 @@ function import_blend_material_run(path: string) {
 			let search: string = bl_handle_get(node, "idname");
 			search = to_lower_case(substring(search, 10, search.length));
 			let base: zui_node_t = null;
-			for (let list of nodes_material_list) {
+			for (let i: i32 = 0; i < nodes_material_list.length; ++i) {
+				let list: zui_node_t[] = nodes_material_list[i];
 				let found: bool = false;
-				for (let n of list) {
+				for (let i: i32 = 0; i < list.length; ++i) {
+					let n: zui_node_t = list[i];
 					let s: string = to_lower_case(string_replace_all(n.type, "_", ""));
 					if (search == s) {
 						base = n;
@@ -197,13 +202,15 @@ function import_blend_material_run(path: string) {
 
 			let from_id: i32 = -1;
 			let to_id: i32 = -1;
-			for (let n of canvas.nodes) {
+			for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+				let n: zui_node_t = canvas.nodes[i];
 				if (n.name == fromnode) {
 					from_id = n.id;
 					break;
 				}
 			}
-			for (let n of canvas.nodes) {
+			for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+				let n: zui_node_t = canvas.nodes[i];
 				if (n.name == tonode) {
 					to_id = n.id;
 					break;
@@ -285,7 +292,8 @@ function import_blend_material_run(path: string) {
 	}
 
 	let _init = function () {
-		for (let m of imported) {
+		for (let i: i32 = 0; i < imported.length; ++i) {
+			let m: slot_material_t = imported[i];
 			context_set_material(m);
 			make_material_parse_paint_material();
 			util_render_make_material_preview();

+ 17 - 16
base/Sources/import_blend_mesh.ts

@@ -3,20 +3,21 @@ let import_blend_mesh_eps: f32 = 1.0 / 32767;
 
 function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 	let b: buffer_t = data_get_blob(path);
-	let bl: BlendRaw = parser_blend_init(b);
+	let bl: blend_t = parser_blend_init(b);
 	if (bl.dna == null) {
 		console_error(strings_error3());
 		return;
 	}
 
-	let obs: BlHandleRaw[] = parser_blend_get(bl, "Object");
+	let obs: bl_handle_t[] = parser_blend_get(bl, "Object");
 	if (obs == null || obs.length == 0) {
 		import_mesh_make_mesh(null, path);
 		return;
 	}
 
 	let first: bool = true;
-	for (let ob of obs) {
+	for (let i: i32 = 0; i < obs.length; ++i) {
+		let ob: bl_handle_t = obs[i];
 		if (bl_handle_get(ob, "type") != 1) {
 			continue;
 		}
@@ -99,8 +100,8 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 			let loopstart: i32 = bl_handle_get(poly, "loopstart");
 			let totloop: i32 = bl_handle_get(poly, "totloop");
 			if (totloop <= 4) { // Convex, fan triangulation
-				let v0: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart + totloop - 1);
-				let v1: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart);
+				let v0: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart + totloop - 1);
+				let v1: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart);
 				let co0: any = bl_handle_get(v0, "co");
 				let co1: any = bl_handle_get(v1, "co");
 				let no0: any = bl_handle_get(v0, "no");
@@ -150,7 +151,7 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 					col1b = parser_blend_read_i8(bl);
 				}
 				for (let j: i32 = 0; j < totloop - 2; ++j) {
-					let v2: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart + j + 1);
+					let v2: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart + j + 1);
 					let co2: any = bl_handle_get(v2, "co");
 					let no2: any = bl_handle_get(v2, "no");
 					if (smooth) {
@@ -229,9 +230,9 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 				for (let i: i32 = 0; i < totloop; ++i) {
 					array_push(va, loopstart + i);
 				}
-				let v0: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart);
-				let v1: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart + 1);
-				let v2: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart + 2);
+				let v0: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart);
+				let v1: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart + 1);
+				let v2: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart + 2);
 				let co0: any = bl_handle_get(v0, "co");
 				let co1: any = bl_handle_get(v1, "co");
 				let co2: any = bl_handle_get(v2, "co");
@@ -256,8 +257,8 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 
 				let winding: f32 = 0.0;
 				for (let i: i32 = 0; i < totloop; ++i) {
-					let v0: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart + i);
-					let v1: BlHandleRaw = import_blend_mesh_get_mvert_v(m, loopstart + ((i + 1) % totloop));
+					let v0: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart + i);
+					let v1: bl_handle_t = import_blend_mesh_get_mvert_v(m, loopstart + ((i + 1) % totloop));
 					let co0: any = bl_handle_get(v0, "co");
 					let co1: any = bl_handle_get(v1, "co");
 					winding += (co1[axis0] - co0[axis0]) * (co1[axis1] + co0[axis1]);
@@ -273,9 +274,9 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 					i = (i + 1) % vi;
 					let i1: i32 = (i + 1) % vi;
 					let i2: i32 = (i + 2) % vi;
-					let v0: BlHandleRaw = import_blend_mesh_get_mvert_v(m, va[i ]);
-					let v1: BlHandleRaw = import_blend_mesh_get_mvert_v(m, va[i1]);
-					let v2: BlHandleRaw = import_blend_mesh_get_mvert_v(m, va[i2]);
+					let v0: bl_handle_t = import_blend_mesh_get_mvert_v(m, va[i ]);
+					let v1: bl_handle_t = import_blend_mesh_get_mvert_v(m, va[i1]);
+					let v2: bl_handle_t = import_blend_mesh_get_mvert_v(m, va[i2]);
 					let co0: any = bl_handle_get(v0, "co");
 					let co1: any = bl_handle_get(v1, "co");
 					let co2: any = bl_handle_get(v2, "co");
@@ -298,7 +299,7 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 					let overlap: bool = false; // Other vertex found inside this triangle
 					for (let j: i32 = 0; j < vi - 3; ++j) {
 						let j0: i32 = (i + 3 + j) % vi;
-						let v: BlHandleRaw = import_blend_mesh_get_mvert_v(m, va[j0]);
+						let v: bl_handle_t = import_blend_mesh_get_mvert_v(m, va[j0]);
 						let co: any = bl_handle_get(v, "co");
 						let px: f32 = co[axis0];
 						let py: f32 = co[axis1];
@@ -494,6 +495,6 @@ function import_blend_mesh_run(path: string, replace_existing: bool = true) {
 	data_delete_blob(path);
 }
 
-function import_blend_mesh_get_mvert_v(m: BlHandleRaw, loopstart: i32) {
+function import_blend_mesh_get_mvert_v(m: bl_handle_t, loopstart: i32) {
 	return bl_handle_get(m, "mvert", bl_handle_get(bl_handle_get(m, "mloop", loopstart), "v"));
 }

+ 3 - 3
base/Sources/import_envmap.ts

@@ -56,11 +56,11 @@ function import_envmap_run(path: string, image: image_t) {
 
 	// Radiance
 	if (import_envmap_mips_cpu != null) {
-		for (let mip of import_envmap_mips_cpu) {
-			let _mip: image_t = mip;
+		for (let i: i32 = 0; i < import_envmap_mips_cpu.length; ++i) {
+			let mip: image_t = import_envmap_mips_cpu[i];
 			base_notify_on_next_frame(function () {
 				///if (!krom_direct3d12) // TODO: crashes after 50+ imports
-				image_unload(_mip);
+				image_unload(mip);
 				///end
 			});
 		}

+ 4 - 2
base/Sources/import_font.ts

@@ -2,7 +2,8 @@
 ///if (is_paint || is_sculpt)
 
 function import_font_run(path: string) {
-	for (let f of project_fonts) {
+	for (let i: i32 = 0; i < project_fonts.length; ++i) {
+		let f: slot_font_t = project_fonts[i];
 		if (f.file == path) {
 			console_info(strings_info0());
 			return;
@@ -22,7 +23,8 @@ function import_font_run(path: string) {
 	}
 
 	let _init = function () {
-		for (let f of font_slots) {
+		for (let i: i32 = 0; i < font_slots.length; ++i) {
+			let f: slot_font_t = font_slots[i];
 			context_raw.font = f;
 			array_push(project_fonts, f);
 			util_render_make_font_preview();

+ 6 - 3
base/Sources/import_mesh.ts

@@ -83,7 +83,8 @@ function import_mesh_finish_import() {
 		});
 
 		// No mask by default
-		for (let p of project_paint_objects) {
+		for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+			let p: mesh_object_t = project_paint_objects[i];
 			p.base.visible = true;
 		}
 		if (context_raw.merged_object == null) {
@@ -187,7 +188,8 @@ function import_mesh_make_mesh(mesh: any, path: string) {
 		}
 		let first_unwrap_done = function (mesh: any) {
 			_import_mesh_make_mesh(mesh);
-			for (let mesh of import_mesh_meshes_to_unwrap) {
+			for (let i: i32 = 0; i < import_mesh_meshes_to_unwrap.length; ++i) {
+				let mesh: any = import_mesh_meshes_to_unwrap[i];
 				project_unwrap_mesh_box(mesh, _import_mesh_add_mesh, true);
 			}
 		}
@@ -211,7 +213,8 @@ function _import_mesh_add_mesh(mesh: any) {
 	object.skip_context = "paint";
 
 	// Ensure unique names
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		if (p.base.name == object.base.name) {
 			p.base.name += ".001";
 			p.data._.handle += ".001";

+ 2 - 1
base/Sources/import_texture.ts

@@ -7,7 +7,8 @@ function import_texture_run(path: string, hdr_as_envmap: bool = true) {
 		}
 	}
 
-	for (let a of project_assets) {
+	for (let i: i32 = 0; i < project_assets.length; ++i) {
+		let a: asset_t = project_assets[i];
 		// Already imported
 		if (a.file == path) {
 			// Set as envmap

+ 8 - 4
base/Sources/main.ts

@@ -107,10 +107,12 @@ function main_embed(additional: string[]) {
 		"text_coloring.json",
 		"version.json"
 	];
-	for (let add of additional) {
+	for (let i: i32 = 0; i < additional.length; ++i) {
+		let add: string = additional[i];
 		array_push(files, add);
 	}
-	for (let file of files) {
+	for (let i: i32 = 0; i < files.length; ++i) {
+		let file: string = files[i];
 		resource_embed_blob(file, global["data/" + file]);
 		global["data/" + file] = null;
 	}
@@ -127,7 +129,8 @@ function main_embed_raytrace() {
 		"raytrace_brute_core" + render_path_raytrace_ext,
 		"raytrace_brute_full" + render_path_raytrace_ext
 	];
-	for (let file of files) {
+	for (let i: i32 = 0; i < files.length; ++i) {
+		let file: string = files[i];
 		resource_embed_blob(file, global["data/" + file]);
 		global["data/" + file] = null;
 	}
@@ -141,7 +144,8 @@ function main_embed_raytrace_bake() {
 		"raytrace_bake_light" + render_path_raytrace_ext,
 		"raytrace_bake_thick" + render_path_raytrace_ext
 	];
-	for (let file of files) {
+	for (let i: i32 = 0; i < files.length; ++i) {
+		let file: string = files[i];
 		resource_embed_blob(file, global["data/" + file]);
 		global["data/" + file] = null;
 	}

+ 110 - 64
base/Sources/node_shader.ts

@@ -1,46 +1,63 @@
 
-class node_shader_t {
-	context: node_shader_context_t;
-	shader_type: string = '';
-	includes: string[] = [];
-	ins: string[] = [];
-	outs: string[] = [];
-	shared_samplers: string[] = [];
-	uniforms: string[] = [];
-	functions: map_t<string, string> = map_create();
-	main: string = '';
-	main_init: string = '';
-	main_end: string = '';
-	main_normal: string = '';
-	main_textures: string = '';
-	main_attribs: string = '';
-	header: string = '';
-	write_pre: bool = false;
-	write_normal: i32 = 0;
-	write_textures: i32 = 0;
-	vstruct_as_vsin: bool = true;
-	lock: bool = false;
+type node_shader_t = {
+	context?: node_shader_context_t;
+	shader_type?: string;
+	includes?: string[];
+	ins?: string[];
+	outs?: string[];
+	shared_samplers?: string[];
+	uniforms?: string[];
+	functions?: map_t<string, string>;
+	main?: string;
+	main_init?: string;
+	main_end?: string;
+	main_normal?: string;
+	main_textures?: string;
+	main_attribs?: string;
+	header?: string;
+	write_pre?: bool;
+	write_normal?: i32;
+	write_textures?: i32;
+	vstruct_as_vsin?: bool;
+	lock?: bool;
 
 	// References
-	bposition: bool = false;
-	wposition: bool = false;
-	mposition: bool = false;
-	vposition: bool = false;
-	wvpposition: bool = false;
-	ndcpos: bool = false;
-	wtangent: bool = false;
-	vvec: bool = false;
-	vvec_cam: bool = false;
-	n: bool = false;
-	nattr: bool = false;
-	dotnv: bool = false;
-	inv_tbn: bool = false;
-}
+	bposition?: bool;
+	wposition?: bool;
+	mposition?: bool;
+	vposition?: bool;
+	wvpposition?: bool;
+	ndcpos?: bool;
+	wtangent?: bool;
+	vvec?: bool;
+	vvec_cam?: bool;
+	n?: bool;
+	nattr?: bool;
+	dotnv?: bool;
+	inv_tbn?: bool;
+};
 
 function node_shader_create(context: node_shader_context_t, shader_type: string): node_shader_t {
-	let raw: node_shader_t = new node_shader_t();
+	let raw: node_shader_t = {};
 	raw.context = context;
 	raw.shader_type = shader_type;
+	raw.includes = [];
+	raw.ins = [];
+	raw.outs = [];
+	raw.shared_samplers = [];
+	raw.uniforms = [];
+	raw.functions = map_create();
+	raw.main = '';
+	raw.main_init = '';
+	raw.main_end = '';
+	raw.main_normal = '';
+	raw.main_textures = '';
+	raw.main_attribs = '';
+	raw.header = '';
+	raw.write_pre = false;
+	raw.write_normal = 0;
+	raw.write_textures = 0;
+	raw.vstruct_as_vsin = true;
 	return raw;
 }
 
@@ -172,7 +189,8 @@ function node_shader_vstruct_to_vsin(raw: node_shader_t) {
 	// if self.shader_type != 'vert' or self.ins != [] or not self.vstruct_as_vsin: # Vertex structure as vertex shader input
 		// return
 	let vs: vertex_element_t[] = raw.context.data.vertex_elements;
-	for (let e of vs) {
+	for (let i: i32 = 0; i < vs.length; ++i) {
+		let e: vertex_element_t = vs[i];
 		node_shader_add_in(raw, 'vec' + node_shader_data_size(raw, e.data) + ' ' + e.name);
 	}
 }
@@ -213,7 +231,8 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 	let in_ext: string = '';
 	let out_ext: string = '';
 
-	for (let a of raw.includes) {
+	for (let i: i32 = 0; i < raw.includes.length; ++i) {
+		let a: string = raw.includes[i];
 		s += '#include "' + a + '"\n';
 	}
 
@@ -226,7 +245,8 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 			// Sort inputs by name
 			return substring(a, 4, a.length) >= substring(b, 4, b.length) ? 1 : -1;
 		});
-		for (let a of raw.ins) {
+		for (let i: i32 = 0; i < raw.ins.length; ++i) {
+			let a: string = raw.ins[i];
 			s += `${a}${in_ext} : TEXCOORD${index};\n`;
 			index++;
 		}
@@ -252,7 +272,8 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 		});
 		index = 0;
 		if (raw.shader_type == 'vert') {
-			for (let a of raw.outs) {
+			for (let i: i32 = 0; i < raw.outs.length; ++i) {
+				let a: string = raw.outs[i];
 				s += `${a}${out_ext} : TEXCOORD${index};\n`;
 				index++;
 			}
@@ -262,7 +283,7 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 			let out: string = raw.outs[0];
 			// Multiple render targets
 			if (char_at(out, out.length - 1) == ']') {
-				num = parseInt(char_at(out, out.length - 2));
+				num = parse_int(char_at(out, out.length - 2));
 				s += `vec4 fragColor[${num}] : SV_TARGET0;\n`;
 			}
 			else {
@@ -272,7 +293,8 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 		s += '};\n';
 	}
 
-	for (let a of raw.uniforms) {
+	for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
+		let a: string = raw.uniforms[i];
 		s += 'uniform ' + a + ';\n';
 		if (starts_with(a, 'sampler')) {
 			s += 'SamplerState ' + string_split(a, ' ')[1] + '_sampler;\n';
@@ -280,13 +302,16 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 	}
 
 	if (raw.shared_samplers.length > 0) {
-		for (let a of raw.shared_samplers) {
+		for (let i: i32 = 0; o < raw.shared_samplers.length; ++i) {
+			let a: string = raw.shared_samplers[i];
 			s += 'uniform ' + a + ';\n';
 		}
 		s += `SamplerState ${shared_sampler};\n`;
 	}
 
-	for (let f of raw.functions.values()) {
+	let values: string[] = map_to_array(raw.functions);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let f: string = values[i];
 		s += f + '\n';
 	}
 
@@ -309,14 +334,16 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 	}
 
 	// Declare inputs
-	for (let a of raw.ins) {
+	for (let i: i32 = 0; i < raw.ins.length; ++i) {
+		let a: string = raw.ins[i];
 		let b: string = substring(a, 5, a.length); // Remove type 'vec4 '
 		s += `${a} = stage_input.${b};\n`;
 	}
 
 	if (raw.shader_type == 'vert') {
 		s += 'vec4 gl_Position;\n';
-		for (let a of raw.outs) {
+		for (let i: i32 = 0; i < raw.outs.length; ++i) {
+			let a: string = raw.outs[i];
 			s += `${a};\n`;
 		}
 	}
@@ -344,7 +371,8 @@ function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): strin
 		if (raw.shader_type == 'vert') {
 			s += 'gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n';
 			s += 'stage_output.svpos = gl_Position;\n';
-			for (let a of raw.outs) {
+			for (let i: i32 = 0; i < raw.outs.length; ++i) {
+				let a: string = raw.outs[i];
 				let b: string = substring(a, 5, a.length); // Remove type 'vec4 '
 				s += `stage_output.${b} = ${b};\n`;
 			}
@@ -400,7 +428,8 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 	s += '#define mul(a, b) b * a\n';
 	s += '#define discard discard_fragment()\n';
 
-	for (let a of raw.includes) {
+	for (let i: i32 = 0; i < raw.includes.length; ++i) {
+		let a: string = raw.includes[i];
 		s += '#include "' + a + '"\n';
 	}
 
@@ -416,13 +445,15 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 			return substring(a, 4, a.length) >= substring(b, 4, b.length) ? 1 : -1;
 		});
 		if (raw.shader_type == 'vert') {
-			for (let a of raw.ins) {
+			for (let i: i32 = 0; i < raw.ins.length; ++i) {
+				let a: string = raw.ins[i];
 				s += `${a} [[attribute(${index})]];\n`;
 				index++;
 			}
 		}
 		else {
-			for (let a of raw.ins) {
+			for (let i: i32 = 0; i < raw.ins.length; ++i) {
+				let a: string = raw.ins[i];
 				s += `${a} [[user(locn${index})]];\n`;
 				index++;
 			}
@@ -440,7 +471,8 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 		});
 		index = 0;
 		if (raw.shader_type == 'vert') {
-			for (let a of raw.outs) {
+			for (let i: i32 = 0; i < raw.outs.length; ++i) {
+				let a: string = raw.outs[i];
 				s += `${a} [[user(locn${index})]];\n`;
 				index++;
 			}
@@ -450,7 +482,7 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 			let out: string = raw.outs[0];
 			// Multiple render targets
 			if (char_at(out, out.length - 1) == ']') {
-				num = parseInt(char_at(out, out.length - 2));
+				num = parse_int(char_at(out, out.length - 2));
 				for (let i: i32 = 0; i < num; ++i) {
 					s += `float4 fragColor_${i} [[color(${i})]];\n`;
 				}
@@ -467,7 +499,8 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 	if (raw.uniforms.length > 0) {
 		s += 'struct main_uniforms {\n';
 
-		for (let a of raw.uniforms) {
+		for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
+			let a: string = raw.uniforms[i];
 			if (starts_with(a, 'sampler')) {
 				array_push(samplers, a);
 			}
@@ -479,7 +512,9 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 		s += '};\n';
 	}
 
-	for (let f of raw.functions.values()) {
+	let values: string[] = map_to_array(raw.functions);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let f: string = values[i];
 		s += f + '\n';
 	}
 
@@ -525,12 +560,14 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 	s += '#define texture(tex, coord) tex.sample(tex ## _sampler, coord)\n';
 
 	// Declare inputs
-	for (let a of raw.ins) {
+	for (let i: i32 = 0; i < raw.ins.length; ++i) {
+		let a: string = raw.ins[i];
 		let b: string = substring(a, 5, a.length); // Remove type 'vec4 '
 		s += `${a} = in.${b};\n`;
 	}
 
-	for (let a of raw.uniforms) {
+	for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
+		let a: string = raw.uniforms[i];
 		if (!starts_with(a, 'sampler')) {
 			let b: string = string_split(a, " ")[1]; // Remove type 'vec4 '
 			if (string_index_of(b, "[") >= 0) {
@@ -546,7 +583,8 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 
 	if (raw.shader_type == 'vert') {
 		s += 'vec4 gl_Position;\n';
-		for (let a of raw.outs) {
+		for (let i: i32 = 0; i < raw.outs.length; ++i) {
+			let a: string = raw.outs[i];
 			s += `${a};\n`;
 		}
 	}
@@ -574,7 +612,8 @@ function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string
 		if (raw.shader_type == 'vert') {
 			s += 'gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n';
 			s += 'out.svpos = gl_Position;\n';
-			for (let a of raw.outs) {
+			for (let i: i32 = 0; i < raw.outs.length; ++i) {
+				let a: string = raw.outs[i];
 				let b: string = string_split(a, " ")[1]; // Remove type 'vec4 '
 				s += `out.${b} = ${b};\n`;
 			}
@@ -610,22 +649,29 @@ function node_shader_get_glsl(raw: node_shader_t, shared_sampler: string, versio
 	let in_ext: string = '';
 	let out_ext: string = '';
 
-	for (let a of raw.includes) {
+	for (let i: i32 = 0; i < raw.includes.length; ++i) {
+		let a: string = raw.includes[i];
 		s += '#include "' + a + '"\n';
 	}
-	for (let a of raw.ins) {
+	for (let i: i32 = 0; i < raw.ins.length; ++i) {
+		let a: string = raw.ins[i];
 		s += `in ${a}${in_ext};\n`;
 	}
-	for (let a of raw.outs) {
+	for (let i: i32 = 0; i < raw.outs.length; ++i) {
+		let a: string = raw.outs[i];
 		s += `out ${a}${out_ext};\n`;
 	}
-	for (let a of raw.uniforms) {
+	for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
+		let a: string = raw.uniforms[i];
 		s += 'uniform ' + a + ';\n';
 	}
-	for (let a of raw.shared_samplers) {
+	for (let i: i32 = 0; i < raw.shared_samplers.length; ++i) {
+		let a: string = raw.shared_samplers[i];
 		s += 'uniform ' + a + ';\n';
 	}
-	for (let f of raw.functions.values()) {
+	let values: string[] = map_to_array(raw.functions);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let f: string = values[i];
 		s += f + '\n';
 	}
 	s += 'void main() {\n';

+ 20 - 15
base/Sources/node_shader_context.ts

@@ -1,16 +1,16 @@
 
-class node_shader_context_t {
-	vert: node_shader_t;
-	frag: node_shader_t;
-	data: shader_context_t;
-	allow_vcols: bool = false;
-	material: material_t;
-	constants: shader_const_t[];
-	tunits: tex_unit_t[];
-}
+type node_shader_context_t = {
+	vert?: node_shader_t;
+	frag?: node_shader_t;
+	data?: shader_context_t;
+	allow_vcols?: bool;
+	material?: material_t;
+	constants?: shader_const_t[];
+	tunits?: tex_unit_t[];
+};
 
 function node_shader_context_create(material: material_t, props: any): node_shader_context_t {
-	let raw: node_shader_context_t = new node_shader_context_t();
+	let raw: node_shader_context_t = {};
 	raw.material = material;
 	raw.data = {
 		name: props.name,
@@ -49,7 +49,8 @@ function node_shader_context_create(material: material_t, props: any): node_shad
 }
 
 function node_shader_context_add_elem(raw: node_shader_context_t, name: string, data_type: string) {
-	for (let e of raw.data.vertex_elements) {
+	for (let i: i32 = 0; i < raw.data.vertex_elements.length; ++i) {
+		let e: vertex_element_t = raw.data.vertex_elements[i];
 		if (e.name == name) {
 			return;
 		}
@@ -59,7 +60,8 @@ function node_shader_context_add_elem(raw: node_shader_context_t, name: string,
 }
 
 function node_shader_context_is_elem(raw: node_shader_context_t, name: string): bool {
-	for (let elem of raw.data.vertex_elements) {
+	for (let i: i32 = 0; i < raw.data.vertex_elements.length; ++i) {
+		let elem: vertex_element_t = raw.data.vertex_elements[i];
 		if (elem.name == name) {
 			return true;
 		}
@@ -68,7 +70,8 @@ function node_shader_context_is_elem(raw: node_shader_context_t, name: string):
 }
 
 function node_shader_context_get_elem(raw: node_shader_context_t, name: string): vertex_element_t {
-	for (let elem of raw.data.vertex_elements) {
+	for (let i: i32 = 0; i < raw.data.vertex_elements.length; ++i) {
+		let elem: vertex_element_t = raw.data.vertex_elements[i];
 		if (elem.name == name) {
 			return elem;
 		}
@@ -77,7 +80,8 @@ function node_shader_context_get_elem(raw: node_shader_context_t, name: string):
 }
 
 function node_shader_context_add_constant(raw: node_shader_context_t, ctype: string, name: string, link: string = null) {
-	for (let c of raw.constants) {
+	for (let i: i32 = 0; i < raw.constants.length; ++i) {
+		let c: shader_const_t = raw.constants[i];
 		if (c.name == name) {
 			return;
 		}
@@ -91,7 +95,8 @@ function node_shader_context_add_constant(raw: node_shader_context_t, ctype: str
 }
 
 function node_shader_context_add_texture_unit(raw: node_shader_context_t, ctype: string, name: string, link: string = null, is_image: bool = false) {
-	for (let c of raw.tunits) {
+	for (let i: i32 = 0; i < raw.tunits.length; ++i) {
+		let c: tex_unit_t = raw.tunits[i];
 		if (c.name == name) {
 			return;
 		}

+ 2 - 2
base/Sources/nodes/boolean_node.ts

@@ -4,12 +4,12 @@ type boolean_node_t = {
 	value?: bool;
 };
 
-function boolean_node_create(value: bool = false): boolean_node_t {
+function boolean_node_create(arg: bool): boolean_node_t {
 	let n: boolean_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = boolean_node_get;
 	n.base.set = boolean_node_set;
-	n.value = value;
+	n.value = arg;
 	return n;
 }
 

+ 5 - 1
base/Sources/nodes/color_node.ts

@@ -5,7 +5,11 @@ type color_node_t = {
 	image?: image_t;
 };
 
-function color_node_create(r: f32 = 0.8, g: f32 = 0.8, b: f32 = 0.8, a: f32 = 1.0): color_node_t {
+function color_node_create(args: any): color_node_t {
+	let r: f32 = args == null ? 0.8 : args[0];
+	let g: f32 = args == null ? 0.8 : args[1];
+	let b: f32 = args == null ? 0.8 : args[2];
+	let a: f32 = args == null ? 1.0 : args[3];
 	let n: color_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = color_node_get;

+ 2 - 2
base/Sources/nodes/float_node.ts

@@ -5,13 +5,13 @@ type float_node_t = {
 	image?: image_t;
 };
 
-function float_node_create(value: f32 = 0.0): float_node_t {
+function float_node_create(arg: f32): float_node_t {
 	let n: float_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = float_node_get;
 	n.base.get_as_image = float_node_get_as_image;
 	n.base.set = float_node_set;
-	n.value = value;
+	n.value = arg;
 	return n;
 }
 

+ 2 - 2
base/Sources/nodes/integer_node.ts

@@ -4,12 +4,12 @@ type integer_node_t = {
 	value?: i32;
 };
 
-function integer_node_create(value: i32 = 0): integer_node_t {
+function integer_node_create(arg: i32): integer_node_t {
 	let n: float_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = integer_node_get;
 	n.base.set = integer_node_set;
-	n.value = value;
+	n.value = arg;
 	return n;
 }
 

+ 106 - 107
base/Sources/nodes/math_node.ts

@@ -5,7 +5,7 @@ type math_node_t = {
 	use_clamp?: bool;
 };
 
-function math_node_create(): math_node_t {
+function math_node_create(arg: any): math_node_t {
 	let n: math_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = math_node_get;
@@ -16,112 +16,111 @@ function math_node_get(self: math_node_t, from: i32, done: (a: any)=>void) {
 	logic_node_input_get(self.base.inputs[0], function (v1: f32) {
 		logic_node_input_get(self.base.inputs[1], function (v2: f32) {
 			let f: f32 = 0.0;
-			switch (self.operation) {
-				case "Add":
-					f = v1 + v2;
-					break;
-				case "Multiply":
-					f = v1 * v2;
-					break;
-				case "Sine":
-					f = math_sin(v1);
-					break;
-				case "Cosine":
-					f = math_cos(v1);
-					break;
-				case "Max":
-					f = math_max(v1, v2);
-					break;
-				case "Min":
-					f = math_min(v1, v2);
-					break;
-				case "Absolute":
-					f = math_abs(v1);
-					break;
-				case "Subtract":
-					f = v1 - v2;
-					break;
-				case "Divide":
-					f = v1 / (v2 == 0.0 ? 0.000001 : v2);
-					break;
-				case "Tangent":
-					f = math_tan(v1);
-					break;
-				case "Arcsine":
-					f = math_asin(v1);
-					break;
-				case "Arccosine":
-					f = math_acos(v1);
-					break;
-				case "Arctangent":
-					f = math_atan(v1);
-					break;
-				case "Arctan2":
-					f = math_atan2(v2, v1);
-					break;
-				case "Power":
-					f = math_pow(v1, v2);
-					break;
-				case "Logarithm":
-					f = math_log(v1);
-					break;
-				case "Round":
-					f = math_round(v1);
-					break;
-				case "Floor":
-					f = math_floor(v1);
-					break;
-				case "Ceil":
-					f = math_ceil(v1);
-					break;
-				case "Truncate":
-					f = math_floor(v1);
-					break;
-				case "Fraction":
-					f = v1 - math_floor(v1);
-					break;
-				case "Less Than":
-					f = v1 < v2 ? 1.0 : 0.0;
-					break;
-				case "Greater Than":
-					f = v1 > v2 ? 1.0 : 0.0;
-					break;
-				case "Modulo":
-					f = v1 % v2;
-					break;
-				case "Snap":
-					f = math_floor(v1 / v2) * v2;
-					break;
-				case "Square Root":
-					f = math_sqrt(v1);
-					break;
-				case "Inverse Square Root":
-					f = 1.0 / math_sqrt(v1);
-					break;
-				case "Exponent":
-					f = math_exp(v1);
-					break;
-				case "Sign":
-					f = v1 > 0 ? 1.0 : (v1 < 0 ? -1.0 : 0);
-					break;
-				case "Ping-Pong":
-					f = (v2 != 0.0) ? v2 - math_abs((math_abs(v1) % (2 * v2)) - v2) : 0.0;
-					break;
-				case "Hyperbolic Sine":
-					f = (math_exp(v1) - math_exp(-v1)) / 2.0;
-					break;
-				case "Hyperbolic Cosine":
-					f = (math_exp(v1) + math_exp(-v1)) / 2.0;
-					break;
-				case "Hyperbolic Tangent":
-					f = 1.0 - (2.0 / (math_exp(2 * v1) + 1));
-					break;
-				case "To Radians":
-					f = v1 / 180.0 * math_pi();
-					break;
-				case "To Degrees":
-					f = v1 / math_pi() * 180.0;
-					break;
+			let op: string = self.operation;
+			if (op == "Add") {
+				f = v1 + v2;
+			}
+			else if (op == "Multiply") {
+				f = v1 * v2;
+			}
+			else if (op == "Sine") {
+				f = math_sin(v1);
+			}
+			else if (op == "Cosine") {
+				f = math_cos(v1);
+			}
+			else if (op == "Max") {
+				f = math_max(v1, v2);
+			}
+			else if (op == "Min") {
+				f = math_min(v1, v2);
+			}
+			else if (op == "Absolute") {
+				f = math_abs(v1);
+			}
+			else if (op == "Subtract") {
+				f = v1 - v2;
+			}
+			else if (op == "Divide") {
+				f = v1 / (v2 == 0.0 ? 0.000001 : v2);
+			}
+			else if (op == "Tangent") {
+				f = math_tan(v1);
+			}
+			else if (op == "Arcsine") {
+				f = math_asin(v1);
+			}
+			else if (op == "Arccosine") {
+				f = math_acos(v1);
+			}
+			else if (op == "Arctangent") {
+				f = math_atan(v1);
+			}
+			else if (op == "Arctan2") {
+				f = math_atan2(v2, v1);
+			}
+			else if (op == "Power") {
+				f = math_pow(v1, v2);
+			}
+			else if (op == "Logarithm") {
+				f = math_log(v1);
+			}
+			else if (op == "Round") {
+				f = math_round(v1);
+			}
+			else if (op == "Floor") {
+				f = math_floor(v1);
+			}
+			else if (op == "Ceil") {
+				f = math_ceil(v1);
+			}
+			else if (op == "Truncate") {
+				f = math_floor(v1);
+			}
+			else if (op == "Fraction") {
+				f = v1 - math_floor(v1);
+			}
+			else if (op == "Less Than") {
+				f = v1 < v2 ? 1.0 : 0.0;
+			}
+			else if (op == "Greater Than") {
+				f = v1 > v2 ? 1.0 : 0.0;
+			}
+			else if (op == "Modulo") {
+				f = v1 % v2;
+			}
+			else if (op == "Snap") {
+				f = math_floor(v1 / v2) * v2;
+			}
+			else if (op == "Square Root") {
+				f = math_sqrt(v1);
+			}
+			else if (op == "Inverse Square Root") {
+				f = 1.0 / math_sqrt(v1);
+			}
+			else if (op == "Exponent") {
+				f = math_exp(v1);
+			}
+			else if (op == "Sign") {
+				f = v1 > 0 ? 1.0 : (v1 < 0 ? -1.0 : 0);
+			}
+			else if (op == "Ping-Pong") {
+				f = (v2 != 0.0) ? v2 - math_abs((math_abs(v1) % (2 * v2)) - v2) : 0.0;
+			}
+			else if (op == "Hyperbolic Sine") {
+				f = (math_exp(v1) - math_exp(-v1)) / 2.0;
+			}
+			else if (op == "Hyperbolic Cosine") {
+				f = (math_exp(v1) + math_exp(-v1)) / 2.0;
+			}
+			else if (op == "Hyperbolic Tangent") {
+				f = 1.0 - (2.0 / (math_exp(2 * v1) + 1));
+			}
+			else if (op == "To Radians") {
+				f = v1 / 180.0 * math_pi();
+			}
+			else if (op == "To Degrees") {
+				f = v1 / math_pi() * 180.0;
 			}
 
 			if (self.use_clamp) {

+ 1 - 1
base/Sources/nodes/null_node.ts

@@ -3,7 +3,7 @@ type null_node_t = {
 	base?: logic_node_t;
 };
 
-function null_node_create(): null_node_t {
+function null_node_create(arg: any): null_node_t {
 	let n: null_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = float_node_get;

+ 1 - 1
base/Sources/nodes/random_node.ts

@@ -8,7 +8,7 @@ let random_node_b: i32;
 let random_node_c: i32;
 let random_node_d: i32 = -1;
 
-function random_node_create(): random_node_t {
+function random_node_create(arg: any): random_node_t {
 	let n: random_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = random_node_get;

+ 1 - 1
base/Sources/nodes/separate_vector_node.ts

@@ -3,7 +3,7 @@ type separate_vector_node_t = {
 	base?: logic_node_t;
 };
 
-function separate_vector_node_create(): separate_vector_node_t {
+function separate_vector_node_create(arg: any): separate_vector_node_t {
 	let n: separate_vector_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = separate_vector_node_get;

+ 2 - 2
base/Sources/nodes/string_node.ts

@@ -4,12 +4,12 @@ type string_node_t = {
 	value?: string;
 };
 
-function string_node_create(value: string = ""): string_node_t {
+function string_node_create(arg: string): string_node_t {
 	let n: string_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = string_node_get;
 	n.base.set = string_node_set;
-	n.value = value;
+	n.value = arg;
 	return n;
 }
 

+ 1 - 1
base/Sources/nodes/time_node.ts

@@ -3,7 +3,7 @@ type time_node_t = {
 	base?: logic_node_t;
 };
 
-function time_node_create(): time_node_t {
+function time_node_create(arg: any): time_node_t {
 	let n: time_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = time_node_get;

+ 111 - 113
base/Sources/nodes/vector_math_node.ts

@@ -5,7 +5,7 @@ type vector_math_node_t = {
 	v?: vec4_t;
 };
 
-function vector_math_node_create(): vector_math_node_t {
+function vector_math_node_create(arg: any): vector_math_node_t {
 	let n: vector_math_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = vector_math_node_get;
@@ -18,118 +18,116 @@ function vector_math_node_get(self: vector_math_node_t, from: i32, done: (a: any
 		logic_node_input_get(self.base.inputs[1], function (v2: vec4_t) {
 			vec4_set_from(self.v, v1);
 			let f: f32 = 0.0;
-
-			switch (self.operation) {
-				case "Add":
-					vec4_add(self.v, v2);
-					break;
-				case "Subtract":
-					vec4_sub(self.v, v2);
-					break;
-				case "Average":
-					vec4_add(self.v, v2);
-					self.v.x *= 0.5;
-					self.v.y *= 0.5;
-					self.v.z *= 0.5;
-					break;
-				case "Dot Product":
-					f = vec4_dot(self.v, v2);
-					vec4_set(self.v, f, f, f);
-					break;
-				case "Cross Product":
-					vec4_cross(self.v, v2);
-					break;
-				case "Normalize":
-					vec4_normalize(self.v, );
-					break;
-				case "Multiply":
-					self.v.x *= v2.x;
-					self.v.y *= v2.y;
-					self.v.z *= v2.z;
-					break;
-				case "Divide":
-					self.v.x /= v2.x == 0.0 ? 0.000001 : v2.x;
-					self.v.y /= v2.y == 0.0 ? 0.000001 : v2.y;
-					self.v.z /= v2.z == 0.0 ? 0.000001 : v2.z;
-					break;
-				case "Length":
-					f = vec4_len(self.v);
-					vec4_set(self.v, f, f, f);
-					break;
-				case "Distance":
-					f = vec4_dist_to(self.v, v2);
-					vec4_set(self.v, f, f, f);
-					break;
-				case "Project":
-					vec4_set_from(self.v, v2);
-					vec4_mult(self.v, vec4_dot(v1, v2) / vec4_dot(v2, v2));
-					break;
-				case "Reflect":
-					let tmp: vec4_t = vec4_create();
-					vec4_set_from(tmp, v2);
-					vec4_normalize(tmp);
-					vec4_reflect(self.v, tmp);
-					break;
-				case "Scale":
-					self.v.x *= v2.x;
-					self.v.y *= v2.x;
-					self.v.z *= v2.x;
-					break;
-				case "Absolute":
-					self.v.x = math_abs(self.v.x);
-					self.v.y = math_abs(self.v.y);
-					self.v.z = math_abs(self.v.z);
-					break;
-				case "Minimum":
-					self.v.x = math_min(v1.x, v2.x);
-					self.v.y = math_min(v1.y, v2.y);
-					self.v.z = math_min(v1.z, v2.z);
-					break;
-				case "Maximum":
-					self.v.x = math_max(v1.x, v2.x);
-					self.v.y = math_max(v1.y, v2.y);
-					self.v.z = math_max(v1.z, v2.z);
-					break;
-				case "Floor":
-					self.v.x = math_floor(v1.x);
-					self.v.y = math_floor(v1.y);
-					self.v.z = math_floor(v1.z);
-					break;
-				case "Ceil":
-					self.v.x = math_ceil(v1.x);
-					self.v.y = math_ceil(v1.y);
-					self.v.z = math_ceil(v1.z);
-					break;
-				case "Fraction":
-					self.v.x = v1.x - math_floor(v1.x);
-					self.v.y = v1.y - math_floor(v1.y);
-					self.v.z = v1.z - math_floor(v1.z);
-					break;
-				case "Modulo":
-					self.v.x = v1.x % v2.x;
-					self.v.y = v1.y % v2.y;
-					self.v.z = v1.z % v2.z;
-					break;
-				case "Snap":
-					self.v.x = math_floor(v1.x / v2.x) * v2.x;
-					self.v.y = math_floor(v1.y / v2.y) * v2.y;
-					self.v.z = math_floor(v1.z / v2.z) * v2.z;
-					break;
-				case "Sine":
-					self.v.x = math_sin(v1.x);
-					self.v.y = math_sin(v1.y);
-					self.v.z = math_sin(v1.z);
-					break;
-				case "Cosine":
-					self.v.x = math_cos(v1.x);
-					self.v.y = math_cos(v1.y);
-					self.v.z = math_cos(v1.z);
-					break;
-				case "Tangent":
-					self.v.x = math_tan(v1.x);
-					self.v.y = math_tan(v1.y);
-					self.v.z = math_tan(v1.z);
-					break;
+			let op: string = self.operation;
+			if (op == "Add") {
+				vec4_add(self.v, v2);
+			}
+			else if (op == "Subtract") {
+				vec4_sub(self.v, v2);
+			}
+			else if (op == "Average") {
+				vec4_add(self.v, v2);
+				self.v.x *= 0.5;
+				self.v.y *= 0.5;
+				self.v.z *= 0.5;
+			}
+			else if (op == "Dot Product") {
+				f = vec4_dot(self.v, v2);
+				vec4_set(self.v, f, f, f);
+			}
+			else if (op == "Cross Product") {
+				vec4_cross(self.v, v2);
+			}
+			else if (op == "Normalize") {
+				vec4_normalize(self.v, );
+			}
+			else if (op == "Multiply") {
+				self.v.x *= v2.x;
+				self.v.y *= v2.y;
+				self.v.z *= v2.z;
+			}
+			else if (op == "Divide") {
+				self.v.x /= v2.x == 0.0 ? 0.000001 : v2.x;
+				self.v.y /= v2.y == 0.0 ? 0.000001 : v2.y;
+				self.v.z /= v2.z == 0.0 ? 0.000001 : v2.z;
+			}
+			else if (op == "Length") {
+				f = vec4_len(self.v);
+				vec4_set(self.v, f, f, f);
+			}
+			else if (op == "Distance") {
+				f = vec4_dist_to(self.v, v2);
+				vec4_set(self.v, f, f, f);
+			}
+			else if (op == "Project") {
+				vec4_set_from(self.v, v2);
+				vec4_mult(self.v, vec4_dot(v1, v2) / vec4_dot(v2, v2));
+			}
+			else if (op == "Reflect") {
+				let tmp: vec4_t = vec4_create();
+				vec4_set_from(tmp, v2);
+				vec4_normalize(tmp);
+				vec4_reflect(self.v, tmp);
+			}
+			else if (op == "Scale") {
+				self.v.x *= v2.x;
+				self.v.y *= v2.x;
+				self.v.z *= v2.x;
+			}
+			else if (op == "Absolute") {
+				self.v.x = math_abs(self.v.x);
+				self.v.y = math_abs(self.v.y);
+				self.v.z = math_abs(self.v.z);
+			}
+			else if (op == "Minimum") {
+				self.v.x = math_min(v1.x, v2.x);
+				self.v.y = math_min(v1.y, v2.y);
+				self.v.z = math_min(v1.z, v2.z);
+			}
+			else if (op == "Maximum") {
+				self.v.x = math_max(v1.x, v2.x);
+				self.v.y = math_max(v1.y, v2.y);
+				self.v.z = math_max(v1.z, v2.z);
+			}
+			else if (op == "Floor") {
+				self.v.x = math_floor(v1.x);
+				self.v.y = math_floor(v1.y);
+				self.v.z = math_floor(v1.z);
+			}
+			else if (op == "Ceil") {
+				self.v.x = math_ceil(v1.x);
+				self.v.y = math_ceil(v1.y);
+				self.v.z = math_ceil(v1.z);
+			}
+			else if (op == "Fraction") {
+				self.v.x = v1.x - math_floor(v1.x);
+				self.v.y = v1.y - math_floor(v1.y);
+				self.v.z = v1.z - math_floor(v1.z);
+			}
+			else if (op == "Modulo") {
+				self.v.x = v1.x % v2.x;
+				self.v.y = v1.y % v2.y;
+				self.v.z = v1.z % v2.z;
+			}
+			else if (op == "Snap") {
+				self.v.x = math_floor(v1.x / v2.x) * v2.x;
+				self.v.y = math_floor(v1.y / v2.y) * v2.y;
+				self.v.z = math_floor(v1.z / v2.z) * v2.z;
+			}
+			else if (op == "Sine") {
+				self.v.x = math_sin(v1.x);
+				self.v.y = math_sin(v1.y);
+				self.v.z = math_sin(v1.z);
+			}
+			else if (op == "Cosine") {
+				self.v.x = math_cos(v1.x);
+				self.v.y = math_cos(v1.y);
+				self.v.z = math_cos(v1.z);
+			}
+			else if (op == "Tangent") {
+				self.v.x = math_tan(v1.x);
+				self.v.y = math_tan(v1.y);
+				self.v.z = math_tan(v1.z);
 			}
 
 			if (from == 0) {

+ 5 - 5
base/Sources/nodes/vector_node.ts

@@ -5,7 +5,7 @@ type vector_node_t = {
 	image?: image_t;
 };
 
-function vector_node_create(x: Null<f32> = null, y: Null<f32> = null, z: Null<f32> = null): vector_node_t {
+function vector_node_create(args: any): vector_node_t {
 	let n: vector_node_t = {};
 	n.base = logic_node_create();
 	n.base.get = vector_node_get;
@@ -13,10 +13,10 @@ function vector_node_create(x: Null<f32> = null, y: Null<f32> = null, z: Null<f3
 	n.base.set = vector_node_set;
 	n.value = vec4_create();
 
-	if (x != null) {
-		logic_node_add_input(n.base, float_node_create(x).base, 0);
-		logic_node_add_input(n.base, float_node_create(y).base, 0);
-		logic_node_add_input(n.base, float_node_create(z).base, 0);
+	if (args != null) {
+		logic_node_add_input(n.base, float_node_create(args[0]).base, 0);
+		logic_node_add_input(n.base, float_node_create(args[1]).base, 0);
+		logic_node_add_input(n.base, float_node_create(args[2]).base, 0);
 	}
 
 	return n;

+ 32 - 23
base/Sources/nodes_material.ts

@@ -2829,7 +2829,8 @@ function nodes_material_color_ramp_button(ui: zui_t, nodes: zui_nodes_t, node: z
 	// Preview
 	let vals: f32_array_t[] = but.default_value; // [[r, g, b, a, pos], ..]
 	let sw: f32 = ui._w / zui_nodes_SCALE();
-	for (let val of vals) {
+	for (let i: i32 = 0; i < vals.length; ++i) {
+		let val: f32_array_t = vals[i];
 		let pos: f32 = val[4];
 		let col: i32 = color_from_floats(val[0], val[1], val[2], 1.0);
 		zui_fill(pos * sw, 0, (1.0 - pos) * sw, zui_nodes_LINE_H() - 2 * zui_nodes_SCALE(), col);
@@ -2890,7 +2891,8 @@ function nodes_material_new_group_button(ui: zui_t, nodes: zui_nodes_t, node: zu
 			node.name = tr("Group") + " " + i;
 
 			let found: bool = false;
-			for (let g of project_material_groups) {
+			for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+				let g: node_group_t = project_material_groups[i];
 				if (g.canvas.name == node.name) {
 					found = true;
 					break;
@@ -2947,7 +2949,8 @@ function nodes_material_new_group_button(ui: zui_t, nodes: zui_nodes_t, node: zu
 	}
 
 	let group: node_group_t = null;
-	for (let g of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let g: node_group_t = project_material_groups[i];
 		if (g.canvas.name == node.name) {
 			group = g;
 			break;
@@ -2991,29 +2994,33 @@ function nodes_material_add_socket_button(ui: zui_t, nodes: zui_nodes_t, node: z
 function nodes_material_sync_sockets(node: zui_node_t) {
 	let group_stack: node_group_t[] = ui_nodes_group_stack;
 	let c: zui_node_canvas_t = group_stack[group_stack.length - 1].canvas;
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		nodes_material_sync_group_sockets(m.canvas, c.name, node);
 	}
-	for (let g of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let g: node_group_t = project_material_groups[i];
 		nodes_material_sync_group_sockets(g.canvas, c.name, node);
 	}
 	array_push(zui_node_replace, node);
 }
 
-function nodes_material_sync_group_sockets(canvas: zui_node_canvas_t, groupName: string, node: zui_node_t) {
-	for (let n of canvas.nodes) {
-		if (n.type == "GROUP" && n.name == groupName) {
-			let isInputs: bool = node.name == "Group Input";
-			let oldSockets: zui_node_socket_t[] = isInputs ? n.inputs : n.outputs;
-			let sockets: zui_node_socket_t[] = json_parse(json_stringify(isInputs ? node.outputs : node.inputs));
-			isInputs ? n.inputs = sockets : n.outputs = sockets;
-			for (let s of sockets) {
+function nodes_material_sync_group_sockets(canvas: zui_node_canvas_t, group_name: string, node: zui_node_t) {
+	for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+		let n: zui_node_t = canvas.nodes[i];
+		if (n.type == "GROUP" && n.name == group_name) {
+			let is_inputs: bool = node.name == "Group Input";
+			let old_sockets: zui_node_socket_t[] = is_inputs ? n.inputs : n.outputs;
+			let sockets: zui_node_socket_t[] = json_parse(json_stringify(is_inputs ? node.outputs : node.inputs));
+			is_inputs ? n.inputs = sockets : n.outputs = sockets;
+			for (let i: i32 = 0; i < sockets.length; ++i) {
+				let s: zui_node_socket_t = sockets[i];
 				s.node_id = n.id;
 			}
-			let numSockets: i32 = sockets.length < oldSockets.length ? sockets.length : oldSockets.length;
-			for (let i: i32 = 0; i < numSockets; ++i) {
-				if (sockets[i].type == oldSockets[i].type) {
-					sockets[i].default_value = oldSockets[i].default_value;
+			let num_sockets: i32 = sockets.length < old_sockets.length ? sockets.length : old_sockets.length;
+			for (let i: i32 = 0; i < num_sockets; ++i) {
+				if (sockets[i].type == old_sockets[i].type) {
+					sockets[i].default_value = old_sockets[i].default_value;
 				}
 			}
 		}
@@ -3045,10 +3052,12 @@ function nodes_material_create_socket(nodes: zui_nodes_t, node: zui_node_t, name
 	}
 }
 
-function nodes_material_get_node_t(nodeType: string): zui_node_t {
-	for (let c of nodes_material_list) {
-		for (let n of c) {
-			if (n.type == nodeType) {
+function nodes_material_get_node_t(node_type: string): zui_node_t {
+	for (let i: i32 = 0; i < nodes_material_list.length; ++i) {
+		let c: zui_node_t[] = nodes_material_list[i];
+		for (let i: i32 = 0; i < c.length; ++i) {
+			let n: zui_node_t = c[i];
+			if (n.type == node_type) {
 				return n;
 			}
 		}
@@ -3056,8 +3065,8 @@ function nodes_material_get_node_t(nodeType: string): zui_node_t {
 	return null;
 }
 
-function nodes_material_create_node(nodeType: string, group: node_group_t = null): zui_node_t {
-	let n: zui_node_t = nodes_material_get_node_t(nodeType);
+function nodes_material_create_node(node_type: string, group: node_group_t = null): zui_node_t {
+	let n: zui_node_t = nodes_material_get_node_t(node_type);
 	if (n == null) {
 		return null;
 	}

+ 126 - 93
base/Sources/parser_blend.ts

@@ -3,27 +3,28 @@
 // https://github.com/fschutt/mystery-of-the-blend-backup
 // https://web.archive.org/web/20170630054951/http://www.atmind.nl/blender/mystery_ot_blend.html
 // Usage:
-// let bl: BlendRaw = parser_blend_init(blob: buffer_view_t);
+// let bl: blend_t = parser_blend_init(blob: buffer_view_t);
 // krom_log(parser_blend_dir(bl, "Scene"));
 // let scenes: any = parser_blend_get(bl, "Scene");
 // krom_log(get(get(scenes[0], "id"), "name"));
 
-class BlendRaw {
-	pos: i32;
-	view: buffer_view_t;
-
+type blend_t = {
+	pos?: i32;
+	view?: buffer_view_t;
 	// Header
-	version: string;
-	pointer_size: i32;
-	little_endian: bool;
+	version?: string;
+	pointer_size?: i32;
+	little_endian?: bool;
 	// Data
-	blocks: Block[] = [];
-	dna: Dna = null;
-	map: map_t<any, Block> = map_create(); // Map blocks by memory address
-}
-
-function parser_blend_init(buffer: buffer_t): BlendRaw {
-	let raw: BlendRaw = new BlendRaw();
+	blocks?: block_t[];
+	dna?: dna_t;
+	map?: map_t<any, block_t>; // Map blocks by memory address
+};
+
+function parser_blend_init(buffer: buffer_t): blend_t {
+	let raw: blend_t = {};
+	raw.blocks = [];
+	raw.map = map_create();
 	raw.view = buffer_view_create(buffer);
 	raw.pos = 0;
 	if (parser_blend_read_chars(raw, 7) != "BLENDER") {
@@ -37,13 +38,13 @@ function parser_blend_init(buffer: buffer_t): BlendRaw {
 	return raw;
 }
 
-function parser_blend_dir(raw: BlendRaw, type: string): string[] {
+function parser_blend_dir(raw: blend_t, type: string): string[] {
 	// Return structure fields
 	let type_index: i32 = parser_blend_get_type_index(raw.dna, type);
 	if (type_index == -1) {
 		return null;
 	}
-	let ds: DnaStruct = parser_blend_get_struct(raw.dna, type_index);
+	let ds: dna_struct_t = parser_blend_get_struct(raw.dna, type_index);
 	let fields: string[] = [];
 	for (let i: i32 = 0; i < ds.field_names.length; ++i) {
 		let name_index: i32 = ds.field_names[i];
@@ -53,7 +54,7 @@ function parser_blend_dir(raw: BlendRaw, type: string): string[] {
 	return fields;
 }
 
-function parser_blend_get(raw: BlendRaw, type: string): BlHandleRaw[] {
+function parser_blend_get(raw: blend_t, type: string): bl_handle_t[] {
 	if (raw.dna == null) {
 		return null;
 	}
@@ -62,11 +63,13 @@ function parser_blend_get(raw: BlendRaw, type: string): BlHandleRaw[] {
 	if (type_index == -1) {
 		return null;
 	}
-	let ds: DnaStruct = parser_blend_get_struct(raw.dna, type_index);
-	let handles: BlHandleRaw[] = [];
-	for (let b of raw.blocks) {
+	let ds: dna_struct_t = parser_blend_get_struct(raw.dna, type_index);
+	let handles: bl_handle_t[] = [];
+	for (let i: i32 = 0; i < raw.blocks.length; ++i) {
+		let b: block_t = raw.blocks[i];
 		if (raw.dna.structs[b.sdna_index].type == type_index) {
-			let h: BlHandleRaw = new BlHandleRaw();
+			let h: bl_handle_t = {};
+			h.offset = 0;
 			array_push(handles, h);
 			h.block = b;
 			h.ds = ds;
@@ -75,16 +78,17 @@ function parser_blend_get(raw: BlendRaw, type: string): BlHandleRaw[] {
 	return handles;
 }
 
-function parser_blend_get_struct(dna: Dna, typeIndex: i32): DnaStruct {
-	for (let ds of dna.structs) {
-		if (ds.type == typeIndex) {
+function parser_blend_get_struct(dna: dna_t, type_index: i32): dna_struct_t {
+	for (let i: i32 = 0; i < dna.structs.length; ++i) {
+		let ds: dna_struct_t = dna.structs[i];
+		if (ds.type == type_index) {
 			return ds;
 		}
 	}
 	return null;
 }
 
-function parser_blend_get_type_index(dna: Dna, type: string): i32 {
+function parser_blend_get_type_index(dna: dna_t, type: string): i32 {
 	for (let i: i32 = 0; i < dna.types.length; ++i) {
 		if (type == dna.types[i]) {
 			return i;
@@ -93,7 +97,7 @@ function parser_blend_get_type_index(dna: Dna, type: string): i32 {
 	return -1;
 }
 
-function parser_blend_parse(raw: BlendRaw) {
+function parser_blend_parse(raw: blend_t) {
 	// Pointer size: _ 32bit, - 64bit
 	raw.pointer_size = parser_blend_read_char(raw) == "_" ? 4 : 8;
 
@@ -106,7 +110,7 @@ function parser_blend_parse(raw: BlendRaw) {
 	// Header - data
 	while (raw.pos < buffer_view_size(raw.view)) {
 		parser_blend_align(raw);
-		let b: Block = new Block();
+		let b: block_t = {};
 
 		// Block type
 		b.code = parser_blend_read_chars(raw, 4);
@@ -136,7 +140,11 @@ function parser_blend_parse(raw: BlendRaw) {
 
 		// This block stores dna structures
 		if (b.code == "DNA1") {
-			raw.dna = new Dna();
+			raw.dna = {};
+			raw.dna.names = [];
+			raw.dna.types = [];
+			raw.dna.types_length = [];
+			raw.dna.structs = [];
 
 			parser_blend_read_chars(raw, 4); // SDNA
 			parser_blend_read_chars(raw, 4); // NAME
@@ -162,7 +170,7 @@ function parser_blend_parse(raw: BlendRaw) {
 			parser_blend_read_chars(raw, 4); // STRC
 			let struct_count: i32 = parser_blend_read_i32(raw);
 			for (let i: i32 = 0; i < struct_count; ++i) {
-				let ds: DnaStruct = new DnaStruct();
+				let ds: dna_struct_t = {};
 				array_push(raw.dna.structs, ds);
 				ds.dna = raw.dna;
 				ds.type = parser_blend_read_i16(raw);
@@ -183,7 +191,7 @@ function parser_blend_parse(raw: BlendRaw) {
 	}
 }
 
-function parser_blend_align(raw: BlendRaw) {
+function parser_blend_align(raw: blend_t) {
 	// 4 bytes aligned
 	let mod: i32 = raw.pos % 4;
 	if (mod > 0) {
@@ -191,38 +199,38 @@ function parser_blend_align(raw: BlendRaw) {
 	}
 }
 
-function parser_blend_read_i8(raw: BlendRaw): i32 {
+function parser_blend_read_i8(raw: blend_t): i32 {
 	let i: i32 = buffer_view_get_u8(raw.view, raw.pos);
 	raw.pos += 1;
 	return i;
 }
 
-function parser_blend_read_i16(raw: BlendRaw): i32 {
+function parser_blend_read_i16(raw: blend_t): i32 {
 	let i: i32 = buffer_view_get_i16(raw.view, raw.pos); //, raw.little_endian
 	raw.pos += 2;
 	return i;
 }
 
-function parser_blend_read_i32(raw: BlendRaw): i32 {
+function parser_blend_read_i32(raw: blend_t): i32 {
 	let i: i32 = buffer_view_get_i32(raw.view, raw.pos); //, raw.little_endian
 	raw.pos += 4;
 	return i;
 }
 
-function parser_blend_read_i64(raw: BlendRaw): any {
+function parser_blend_read_i64(raw: blend_t): any {
 	let aview: any = raw.view;
 	let i: i32 = aview.getBigInt64(raw.pos, raw.little_endian);
 	raw.pos += 8;
 	return i;
 }
 
-function parser_blend_read_f32(raw: BlendRaw): f32 {
+function parser_blend_read_f32(raw: blend_t): f32 {
 	let f: f32 = buffer_view_get_f32(raw.view, raw.pos); //, raw.little_endian
 	raw.pos += 4;
 	return f;
 }
 
-function parser_blend_read_i8array(raw: BlendRaw, len: i32): i32_array_t {
+function parser_blend_read_i8array(raw: blend_t, len: i32): i32_array_t {
 	let ar: i32_array_t = i32_array_create(len);
 	for (let i: i32 = 0; i < len; ++i) {
 		ar[i] = parser_blend_read_i8(raw);
@@ -230,7 +238,7 @@ function parser_blend_read_i8array(raw: BlendRaw, len: i32): i32_array_t {
 	return ar;
 }
 
-function parser_blend_read_i16array(raw: BlendRaw, len: i32): i32_array_t {
+function parser_blend_read_i16array(raw: blend_t, len: i32): i32_array_t {
 	let ar: i32_array_t = i32_array_create(len);
 	for (let i: i32 = 0; i < len; ++i) {
 		ar[i] = parser_blend_read_i16(raw);
@@ -238,7 +246,7 @@ function parser_blend_read_i16array(raw: BlendRaw, len: i32): i32_array_t {
 	return ar;
 }
 
-function parser_blend_read_i32array(raw: BlendRaw, len: i32): i32_array_t {
+function parser_blend_read_i32array(raw: blend_t, len: i32): i32_array_t {
 	let ar: i32_array_t = i32_array_create(len);
 	for (let i: i32 = 0; i < len; ++i) {
 		ar[i] = parser_blend_read_i32(raw);
@@ -246,7 +254,7 @@ function parser_blend_read_i32array(raw: BlendRaw, len: i32): i32_array_t {
 	return ar;
 }
 
-function parser_blend_read_f32array(raw: BlendRaw, len: i32): f32_array_t {
+function parser_blend_read_f32array(raw: blend_t, len: i32): f32_array_t {
 	let ar: f32_array_t = f32_array_create(len);
 	for (let i: i32 = 0; i < len; ++i) {
 		ar[i] = parser_blend_read_f32(raw);
@@ -254,7 +262,7 @@ function parser_blend_read_f32array(raw: BlendRaw, len: i32): f32_array_t {
 	return ar;
 }
 
-function parser_blend_read_string(raw: BlendRaw): string {
+function parser_blend_read_string(raw: blend_t): string {
 	let s: string = "";
 	while (true) {
 		let ch: i32 = parser_blend_read_i8(raw);
@@ -266,7 +274,7 @@ function parser_blend_read_string(raw: BlendRaw): string {
 	return s;
 }
 
-function parser_blend_read_chars(raw: BlendRaw, len: i32): string {
+function parser_blend_read_chars(raw: blend_t, len: i32): string {
 	let s: string = "";
 	for (let i: i32 = 0; i < len; ++i) {
 		s += parser_blend_read_char(raw);
@@ -274,47 +282,47 @@ function parser_blend_read_chars(raw: BlendRaw, len: i32): string {
 	return s;
 }
 
-function parser_blend_read_char(raw: BlendRaw): string {
+function parser_blend_read_char(raw: blend_t): string {
 	return string_from_char_code(parser_blend_read_i8(raw));
 }
 
-function parser_blend_read_pointer(raw: BlendRaw): any {
+function parser_blend_read_pointer(raw: blend_t): any {
 	return raw.pointer_size == 4 ? parser_blend_read_i32(raw) : parser_blend_read_i64(raw);
 }
 
-class Block {
-	blend: BlendRaw;
-	code: string;
-	size: i32;
-	sdna_index: i32;
-	count: i32;
-	pos: i32; // Byte pos of data start in blob
-}
-
-class Dna {
-	names: string[] = [];
-	types: string[] = [];
-	types_length: i32[] = [];
-	structs: DnaStruct[] = [];
-}
-
-class DnaStruct {
-	dna: Dna;
-	type: i32; // Index in dna.types
-	field_types: i32[]; // Index in dna.types
-	field_names: i32[]; // Index in dna.names
-}
-
-class BlHandleRaw {
-	block: Block;
-	offset: i32 = 0; // Block data bytes offset
-	ds: DnaStruct;
-}
-
-function bl_handle_get_size(raw: BlHandleRaw, index: i32): i32 {
+type block_t = {
+	blend?: blend_t;
+	code?: string;
+	size?: i32;
+	sdna_index?: i32;
+	count?: i32;
+	pos?: i32; // Byte pos of data start in blob
+};
+
+type dna_t = {
+	names?: string[];
+	types?: string[];
+	types_length?: i32[];
+	structs?: dna_struct_t[];
+};
+
+type dna_struct_t = {
+	dna?: dna_t;
+	type?: i32; // Index in dna.types
+	field_types?: i32[]; // Index in dna.types
+	field_names?: i32[]; // Index in dna.names
+};
+
+type bl_handle_t = {
+	block?: block_t;
+	offset?: i32; // Block data bytes offset
+	ds?: dna_struct_t;
+};
+
+function bl_handle_get_size(raw: bl_handle_t, index: i32): i32 {
 	let name_index: i32 = raw.ds.field_names[index];
 	let type_index: i32 = raw.ds.field_types[index];
-	let dna: Dna = raw.ds.dna;
+	let dna: dna_t = raw.ds.dna;
 	let n: string = dna.names[name_index];
 	let size: i32 = 0;
 	if (string_index_of(n, "*") >= 0) {
@@ -340,12 +348,12 @@ function bl_handle_base_name(s: string): string {
 }
 
 function bl_handle_get_array_len(s: string): i32 {
-	return parseInt(substring(s, string_index_of(s, "[") + 1, string_index_of(s, "]")));
+	return parse_int(substring(s, string_index_of(s, "[") + 1, string_index_of(s, "]")));
 }
 
-function bl_handle_get(raw: BlHandleRaw, name: string, index: i32 = 0, as_type: string = null, array_len: i32 = 0): any {
+function bl_handle_get(raw: bl_handle_t, name: string, index: i32 = 0, as_type: string = null, array_len: i32 = 0): any {
 	// Return raw type or structure
-	let dna: Dna = raw.ds.dna;
+	let dna: dna_t = raw.ds.dna;
 	for (let i: i32 = 0; i < raw.ds.field_names.length; ++i) {
 		let name_index: i32 = raw.ds.field_names[i];
 		let dna_name: string = dna.names[name_index];
@@ -367,27 +375,52 @@ function bl_handle_get(raw: BlHandleRaw, name: string, index: i32 = 0, as_type:
 			}
 			// Raw type
 			if (type_index < 12) {
-				let blend: BlendRaw = raw.block.blend;
+				let blend: blend_t = raw.block.blend;
 				blend.pos = raw.block.pos + new_offset;
 				let is_array: bool = char_at(dna_name, dna_name.length - 1) == "]";
 				let len: i32 = is_array ? (array_len > 0 ? array_len : bl_handle_get_array_len(dna_name)) : 1;
-				switch (type) {
-					case "int": return is_array ? parser_blend_read_i32array(blend, len) : parser_blend_read_i32(blend);
-					case "char": return is_array ? parser_blend_read_string(blend) : parser_blend_read_i8(blend);
-					case "uchar": return is_array ? parser_blend_read_i8array(blend, len) : parser_blend_read_i8(blend);
-					case "short": return is_array ? parser_blend_read_i16array(blend, len) : parser_blend_read_i16(blend);
-					case "ushort": return is_array ? parser_blend_read_i16array(blend, len) : parser_blend_read_i16(blend);
-					case "float": return is_array ? parser_blend_read_f32array(blend, len) : parser_blend_read_f32(blend);
-					case "double": return 0; //readf64(blend);
-					case "long": return is_array ? parser_blend_read_i32array(blend, len) : parser_blend_read_i32(blend);
-					case "ulong": return is_array ? parser_blend_read_i32array(blend, len) : parser_blend_read_i32(blend);
-					case "int64_t": return parser_blend_read_i64(blend);
-					case "uint64_t": return parser_blend_read_i64(blend);
-					case "void": if (char_at(dna_name, 0) == "*") { return parser_blend_read_i64(blend); };
+				if (type == "int") {
+					return is_array ? parser_blend_read_i32array(blend, len) : parser_blend_read_i32(blend);
+				}
+				else if (type == "char") {
+					return is_array ? parser_blend_read_string(blend) : parser_blend_read_i8(blend);
+				}
+				else if (type == "uchar") {
+					return is_array ? parser_blend_read_i8array(blend, len) : parser_blend_read_i8(blend);
+				}
+				else if (type == "short") {
+					return is_array ? parser_blend_read_i16array(blend, len) : parser_blend_read_i16(blend);
+				}
+				else if (type == "ushort") {
+					return is_array ? parser_blend_read_i16array(blend, len) : parser_blend_read_i16(blend);
+				}
+				else if (type == "float") {
+					return is_array ? parser_blend_read_f32array(blend, len) : parser_blend_read_f32(blend);
+				}
+				else if (type == "double") {
+					return 0; //readf64(blend);
+				}
+				else if (type == "long") {
+					return is_array ? parser_blend_read_i32array(blend, len) : parser_blend_read_i32(blend);
+				}
+				else if (type == "ulong") {
+					return is_array ? parser_blend_read_i32array(blend, len) : parser_blend_read_i32(blend);
+				}
+				else if (type == "int64_t") {
+					return parser_blend_read_i64(blend);
+				}
+				else if (type == "uint64_t") {
+					return parser_blend_read_i64(blend);
+				}
+				else if (type == "void") {
+					if (char_at(dna_name, 0) == "*") {
+						return parser_blend_read_i64(blend);
+					}
 				}
 			}
 			// Structure
-			let h: BlHandleRaw = new BlHandleRaw();
+			let h: bl_handle_t = {};
+			h.offset = 0;
 			h.ds = parser_blend_get_struct(dna, type_index);
 			let is_pointer: bool = char_at(dna_name, 0) == "*";
 			if (is_pointer) {

+ 35 - 28
base/Sources/parser_logic.ts

@@ -17,7 +17,8 @@ function parser_logic_get_raw_node(node: logic_node_t): zui_node_t {
 }
 
 function parser_logic_get_node(id: i32): zui_node_t {
-	for (let n of parser_logic_nodes) {
+	for (let i: i32 = 0; i < parser_logic_nodes.length; ++i) {
+		let n: zui_node_t = parser_logic_nodes[i];
 		if (n.id == id) {
 			return n;
 		}
@@ -26,7 +27,8 @@ function parser_logic_get_node(id: i32): zui_node_t {
 }
 
 function parser_logic_get_link(id: i32): zui_node_link_t {
-	for (let l of parser_logic_links) {
+	for (let i: i32 = 0; i < parser_logic_links.length; ++i) {
+		let l: zui_node_link_t = parser_logic_links[i];
 		if (l.id == id) {
 			return l;
 		}
@@ -35,7 +37,8 @@ function parser_logic_get_link(id: i32): zui_node_link_t {
 }
 
 function parser_logic_get_input_link(inp: zui_node_socket_t): zui_node_link_t {
-	for (let l of parser_logic_links) {
+	for (let i: i32 = 0; i < parser_logic_links.length; ++i) {
+		let l: zui_node_link_t = parser_logic_links[i];
 		if (l.to_id == inp.node_id) {
 			let node: zui_node_t = parser_logic_get_node(inp.node_id);
 			if (node.inputs.length <= l.to_socket) {
@@ -51,7 +54,8 @@ function parser_logic_get_input_link(inp: zui_node_socket_t): zui_node_link_t {
 
 function parser_logic_get_output_links(out: zui_node_socket_t): zui_node_link_t[] {
 	let res: zui_node_link_t[] = [];
-	for (let l of parser_logic_links) {
+	for (let i: i32 = 0; i < parser_logic_links.length; ++i) {
+		let l: zui_node_link_t = parser_logic_links[i];
 		if (l.from_id == out.node_id) {
 			let node: zui_node_t = parser_logic_get_node(out.node_id);
 			if (node.outputs.length <= l.from_socket) {
@@ -84,7 +88,8 @@ function parser_logic_parse(canvas: zui_node_canvas_t) {
 	parser_logic_raw_map = map_create();
 	let root_nodes: zui_node_t[] = parser_logic_get_root_nodes(canvas);
 
-	for (let node of root_nodes) {
+	for (let i: i32 = 0; i < root_nodes.length; ++i) {
+		let node: zui_node_t = root_nodes[i];
 		parser_logic_build_node(node);
 	}
 }
@@ -101,12 +106,13 @@ function parser_logic_build_node(node: zui_node_t): string {
 	array_push(parser_logic_parsed_nodes, name);
 
 	// Create node
-	let v: any = parser_logic_create_node_instance(node.type, []);
+	let v: any = parser_logic_create_node_instance(node.type, null);
 	map_set(parser_logic_node_map, name, v);
 	map_set(parser_logic_raw_map, v, node);
 
-	// Expose button values in node class
-	for (let b of node.buttons) {
+	// Expose button values in node
+	for (let i: i32 = 0; i < node.buttons.length; ++i) {
+		let b: zui_node_button_t = node.buttons[i];
 		if (b.type == "ENUM") {
 			// let array_data: bool = is_array(b.data);
 			let array_data: bool = b.data.length > 1;
@@ -139,11 +145,13 @@ function parser_logic_build_node(node: zui_node_t): string {
 	}
 
 	// Create outputss
-	for (let out of node.outputs) {
+	for (let i: i32 = 0; i < node.outputs.length; ++i) {
+		let out: zui_node_socket_t = node.outputs[i];
 		let out_nodes: logic_node_t[] = [];
 		let ls: zui_node_link_t[] = parser_logic_get_output_links(out);
 		if (ls != null && ls.length > 0) {
-			for (let l of ls) {
+			for (let i: i32 = 0; i < ls.length; ++i) {
+				let l: zui_node_link_t = ls[i];
 				let n: zui_node_t = parser_logic_get_node(l.to_id);
 				let out_name: string = parser_logic_build_node(n);
 				array_push(out_nodes, map_get(parser_logic_node_map, out_name));
@@ -162,9 +170,11 @@ function parser_logic_build_node(node: zui_node_t): string {
 
 function parser_logic_get_root_nodes(node_group: zui_node_canvas_t): zui_node_t[] {
 	let roots: zui_node_t[] = [];
-	for (let node of node_group.nodes) {
+	for (let i: i32 = 0; i < node_group.nodes.length; ++i) {
+		let node: zui_node_t = node_group.nodes[i];
 		let linked: bool = false;
-		for (let out of node.outputs) {
+		for (let i: i32 = 0; i < node.outputs.length; ++i) {
+			let out: zui_node_socket_t = node.outputs[i];
 			let ls: zui_node_link_t[] = parser_logic_get_output_links(out);
 			if (ls != null && ls.length > 0) {
 				linked = true;
@@ -185,39 +195,39 @@ function parser_logic_build_default_node(inp: zui_node_socket_t): logic_node_t {
 		if (inp.default_value == null) {
 			inp.default_value = [0, 0, 0]; // TODO
 		}
-		v = parser_logic_create_node_instance("vector_node", [inp.default_value[0], inp.default_value[1], inp.default_value[2]]);
+		v = parser_logic_create_node_instance("vector_node", inp.default_value);
 	}
 	else if (inp.type == "RGBA") {
 		if (inp.default_value == null) {
 			inp.default_value = [0, 0, 0, 0]; // TODO
 		}
-		v = parser_logic_create_node_instance("color_node", [inp.default_value[0], inp.default_value[1], inp.default_value[2], inp.default_value[3]]);
+		v = parser_logic_create_node_instance("color_node", inp.default_value);
 	}
 	else if (inp.type == "RGB") {
 		if (inp.default_value == null) {
 			inp.default_value = [0, 0, 0, 0]; // TODO
 		}
-		v = parser_logic_create_node_instance("color_node", [inp.default_value[0], inp.default_value[1], inp.default_value[2], inp.default_value[3]]);
+		v = parser_logic_create_node_instance("color_node", inp.default_value);
 	}
 	else if (inp.type == "VALUE") {
-		v = parser_logic_create_node_instance("float_node", [inp.default_value]);
+		v = parser_logic_create_node_instance("float_node", inp.default_value);
 	}
 	else if (inp.type == "INT") {
-		v = parser_logic_create_node_instance("integer_node", [inp.default_value]);
+		v = parser_logic_create_node_instance("integer_node", inp.default_value);
 	}
 	else if (inp.type == "BOOLEAN") {
-		v = parser_logic_create_node_instance("boolean_node", [inp.default_value]);
+		v = parser_logic_create_node_instance("boolean_node", inp.default_value);
 	}
 	else if (inp.type == "STRING") {
-		v = parser_logic_create_node_instance("string_node", [inp.default_value]);
+		v = parser_logic_create_node_instance("string_node", inp.default_value);
 	}
 	else {
-		v = parser_logic_create_node_instance("null_node", []);
+		v = parser_logic_create_node_instance("null_node", null);
 	}
 	return v;
 }
 
-function parser_logic_create_node_instance(node_type: string, args: any[]): any {
+function parser_logic_create_node_instance(node_type: string, args: any): any {
 	if (map_get(parser_logic_custom_nodes, node_type) != null) {
 		let node: logic_node_t = logic_node_create();
 		node.get = function (from: i32) {
@@ -226,13 +236,10 @@ function parser_logic_create_node_instance(node_type: string, args: any[]): any
 		return node;
 	}
 
-	let eval_args: string = "";
-	for (let arg of args) {
-		if (eval_args != "") {
-			eval_args += ",";
-		}
-		eval_args += arg + "";
+	if (nodes_brush_creates == null) {
+		nodes_brush_init();
 	}
 
-	return eval(node_type + "_create(" + eval_args + ")");
+	let create: any = map_get(nodes_brush_creates, node_type);
+	return create(args);
 }

+ 16 - 8
base/Sources/parser_material.ts

@@ -63,7 +63,8 @@ let parser_material_parsed_map: map_t<string, string> = map_create();
 let parser_material_texture_map: map_t<string, string> = map_create();
 
 function parser_material_get_node(id: i32): zui_node_t {
-	for (let n of parser_material_nodes) {
+	for (let i: i32 = 0; i < parser_material_nodes.length; ++i) {
+		let n: zui_node_t = parser_material_nodes[i];
 		if (n.id == id) {
 			return n;
 		}
@@ -72,7 +73,8 @@ function parser_material_get_node(id: i32): zui_node_t {
 }
 
 function parser_material_get_link(id: i32): zui_node_link_t {
-	for (let l of parser_material_links) {
+	for (let i: i32 = 0; i < parser_material_links.length; ++i) {
+		let l: zui_node_link_t = parser_material_links[i];
 		if (l.id == id) {
 			return l;
 		}
@@ -81,7 +83,8 @@ function parser_material_get_link(id: i32): zui_node_link_t {
 }
 
 function parser_material_get_input_link(inp: zui_node_socket_t): zui_node_link_t {
-	for (let l of parser_material_links) {
+	for (let i: i32 = 0; i < parser_material_links.length; ++i) {
+		let l: zui_node_link_t = parser_material_links[i];
 		if (l.to_id == inp.node_id) {
 			let node: zui_node_t = parser_material_get_node(inp.node_id);
 			if (node.inputs.length <= l.to_socket) {
@@ -97,7 +100,8 @@ function parser_material_get_input_link(inp: zui_node_socket_t): zui_node_link_t
 
 function parser_material_get_output_links(out: zui_node_socket_t): zui_node_link_t[] {
 	let ls: zui_node_link_t[] = null;
-	for (let l of parser_material_links) {
+	for (let i: i32 = 0; i < parser_material_links.length; ++i) {
+		let l: zui_node_link_t = parser_material_links[i];
 		if (l.from_id == out.node_id) {
 			let node: zui_node_t = parser_material_get_node(out.node_id);
 			if (node.outputs.length <= l.from_socket) {
@@ -282,7 +286,8 @@ function parser_material_parse_output_pbr(node: zui_node_t): shader_out_t {
 }
 
 function parser_material_get_group(name: string): zui_node_canvas_t {
-	for (let g of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let g: node_group_t = project_material_groups[i];
 		if (g.canvas.name == name) {
 			return g.canvas;
 		}
@@ -1871,7 +1876,8 @@ function parser_material_to_vec3(s: string): string {
 }
 
 function parser_material_node_by_type(nodes: zui_node_t[], ntype: string): zui_node_t {
-	for (let n of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let n: zui_node_t = nodes[i];
 		if (n.type == ntype) {
 			return n;
 		}
@@ -1893,7 +1899,8 @@ function parser_material_node_name(node: zui_node_t, _parents: zui_node_t[] = nu
 		_parents = parser_material_parents;
 	}
 	let s: string = node.name;
-	for (let p of _parents) {
+	for (let i: i32 = 0; i < _parents.length; ++i) {
+		let p: zui_node_t = _parents[i];
 		s = p.name + p.id + `_` + s;
 	}
 	s = parser_material_safesrc(s) + node.id;
@@ -1921,7 +1928,8 @@ function parser_material_safesrc(s: string): string {
 }
 
 function parser_material_enum_data(s: string): string {
-	for (let a of project_assets) {
+	for (let i: i32 = 0; i < project_assets.length; ++i) {
+		let a: asset_t = project_assets[i];
 		if (a.name == s) {
 			return a.file;
 		}

+ 6 - 3
base/Sources/path.ts

@@ -80,7 +80,8 @@ function path_working_dir(): string {
 
 function path_is_mesh(path: string): bool {
 	let p: string = to_lower_case(path);
-	for (let s of path_mesh_formats) {
+	for (let i: i32 = 0; i < path_mesh_formats.length; ++i) {
+		let s: string = path_mesh_formats[i];
 		if (ends_with(p, "." + s)) {
 			return true;
 		}
@@ -90,7 +91,8 @@ function path_is_mesh(path: string): bool {
 
 function path_is_texture(path: string): bool {
 	let p: string = to_lower_case(path);
-	for (let s of path_texture_formats) {
+	for (let i: i32 = 0; i < path_texture_formats.length; ++i) {
+		let s: string = path_texture_formats[i];
 		if (ends_with(p, "." + s)) {
 			return true;
 		}
@@ -136,7 +138,8 @@ function path_is_known(path: string): bool {
 
 function path_check_ext(p: string, exts: string[]): bool {
 	p = string_replace_all(p, "-", "_");
-	for (let ext of exts) {
+	for (let i: i32 = 0; i < exts.length; ++i) {
+		let ext: string = exts[i];
 		if (ends_with(p, "_" + ext) ||
 			(string_index_of(p, "_" + ext + "_") >= 0 && !ends_with(p, "_preview") && !ends_with(p, "_icon"))) {
 			return true;

+ 99 - 80
base/Sources/physics_body.ts

@@ -1,54 +1,37 @@
 
 ///if arm_physics
 
-class PhysicsBodyRaw {
-	_mass: f32 = 0.0;
-
-	get mass(): f32 {
-		return this._mass;
-	}
-
-	set mass(f: f32) {
-		if (this.ready) {
-			// remove();
-			let t: PhysicsBodyRaw = new PhysicsBodyRaw();
-			t._mass = f;
-			physics_body_init(t, this.object);
-			(this.object as any).physicsBody = t;
-		}
-		else this._mass = f;
-	}
-
-	object: object_t;
-	friction: f32 = 0.5;
-	restitution: f32 = 0.0;
-	collision_margin: f32 = 0.0;
-	linear_damping: f32 = 0.04;
-	angular_damping: f32 = 0.1;
-	linear_factors: f32[] = [1.0, 1.0, 1.0];
-	angular_factors: f32[] = [1.0, 1.0, 1.0];
-	linear_threshold: f32 = 0.0;
-	angular_threshold: f32 = 0.0;
-	ccd: bool = false; // Continuous collision detection
-	trigger: bool = false;
-	group: i32 = 1;
-	mask: i32 = 1;
-	shape: shape_type_t = shape_type_t.BOX;
-	destroyed: bool = false;
-	body_scale_x: f32; // Transform scale at creation time
-	body_scale_y: f32;
-	body_scale_z: f32;
-	current_scale_x: f32;
-	current_scale_y: f32;
-	current_scale_z: f32;
-
-	body: Ammo.btRigidBody = null;
-	motion_state: Ammo.btMotionState;
-	btshape: Ammo.btCollisionShape;
-	ready: bool = false;
-	id: i32 = 0;
-	height_data: u8_array_t = null;
-}
+type physics_body_t = {
+	_mass?: f32;
+	object?: object_t;
+	friction?: f32;
+	restitution?: f32;
+	collision_margin?: f32;
+	linear_damping?: f32;
+	angular_damping?: f32;
+	linear_factors?: f32[];
+	angular_factors?: f32[];
+	linear_threshold?: f32;
+	angular_threshold?: f32;
+	ccd?: bool; // Continuous collision detection
+	trigger?: bool;
+	group?: i32;
+	mask?: i32;
+	shape?: shape_type_t;
+	destroyed?: bool;
+	body_scale_x?: f32; // Transform scale at creation time
+	body_scale_y?: f32;
+	body_scale_z?: f32;
+	current_scale_x?: f32;
+	current_scale_y?: f32;
+	current_scale_z?: f32;
+	body?: Ammo.btRigidBody;
+	motion_state?: Ammo.btMotionState;
+	btshape?: Ammo.btCollisionShape;
+	ready?: bool;
+	id?: i32;
+	height_data?: u8_array_t;
+};
 
 let physics_body_next_id: i32 = 0;
 let physics_body_ammo_array: i32 = -1;
@@ -66,7 +49,7 @@ let physics_body_convex_hull_cache: map_t<mesh_data_t, Ammo.btConvexHullShape> =
 let physics_body_triangle_mesh_cache: map_t<mesh_data_t, Ammo.btTriangleMesh> = map_create();
 let physics_body_users_cache: map_t<mesh_data_t, i32> = map_create();
 
-function physics_body_create(): PhysicsBodyRaw {
+function physics_body_create(): physics_body_t {
 	if (physics_body_first) {
 		physics_body_first = false;
 		physics_body_vec1 = new Ammo.btVector3(0, 0, 0);
@@ -76,15 +59,50 @@ function physics_body_create(): PhysicsBodyRaw {
 		physics_body_trans1 = new Ammo.btTransform();
 		physics_body_trans2 = new Ammo.btTransform();
 	}
-	let pb: PhysicsBodyRaw = new PhysicsBodyRaw();
+	let pb: physics_body_t = {};
+	pb._mass = 0.0;
+	pb.friction = 0.5;
+	pb.restitution = 0.0;
+	pb.collision_margin = 0.0;
+	pb.linear_damping = 0.04;
+	pb.angular_damping = 0.1;
+	pb.linear_factors = [1.0, 1.0, 1.0];
+	pb.angular_factors = [1.0, 1.0, 1.0];
+	pb.linear_threshold = 0.0;
+	pb.angular_threshold = 0.0;
+	pb.ccd = false;
+	pb.trigger = false;
+	pb.group = 1;
+	pb.mask = 1;
+	pb.shape = shape_type_t.BOX;
+	pb.destroyed = false;
+	pb.ready = false;
+	pb.id = 0;
 	return pb;
 }
 
-function physics_body_with_margin(pb: PhysicsBodyRaw, f: f32) {
+function physics_body_get_mass(pb: physics_body_t): f32 {
+	return pb._mass;
+}
+
+function physics_body_set_mass(pb: physics_body_t, f: f32) {
+	if (pb.ready) {
+		// remove();
+		let t: physics_body_t = physics_body_create();
+		t._mass = f;
+		physics_body_init(t, pb.object);
+		(pb.object as any).physicsBody = t;
+	}
+	else {
+		pb._mass = f;
+	}
+}
+
+function physics_body_with_margin(pb: physics_body_t, f: f32) {
 	return f - f * pb.collision_margin;
 }
 
-function physics_body_init(pb: PhysicsBodyRaw, o: object_t) {
+function physics_body_init(pb: physics_body_t, o: object_t) {
 	pb.object = o;
 	if (pb.ready) {
 		return;
@@ -95,7 +113,7 @@ function physics_body_init(pb: PhysicsBodyRaw, o: object_t) {
 		return; // No mesh data
 	}
 	let transform: transform_t = o.transform;
-	let physics: PhysicsWorldRaw = physics_world_active;
+	let physics: physics_world_t = physics_world_active;
 
 	if (pb.shape == shape_type_t.BOX) {
 		physics_world_vec1.setX(physics_body_with_margin(pb, transform.dim.x / 2));
@@ -135,7 +153,7 @@ function physics_body_init(pb: PhysicsBodyRaw, o: object_t) {
 	}
 	else if (pb.shape == shape_type_t.MESH) {
 		let mesh_interface: Ammo.btTriangleMesh = physics_body_fill_triangle_mesh(pb, transform.scale);
-		if (pb.mass > 0) {
+		if (physics_body_get_mass(pb) > 0) {
 			let shape_gimpact: Ammo.btGImpactMeshShape = new Ammo.btGImpactMeshShape(mesh_interface);
 			shape_gimpact.updateBound();
 			let shape_concave: Ammo.btConcaveShape = shape_gimpact;
@@ -187,10 +205,10 @@ function physics_body_init(pb: PhysicsBodyRaw, o: object_t) {
 	physics_body_vec1.setZ(0);
 	let inertia: Ammo.btVector3 = physics_body_vec1;
 
-	if (pb.mass > 0) {
-		pb.btshape.calculateLocalInertia(pb.mass, inertia);
+	if (physics_body_get_mass(pb) > 0) {
+		pb.btshape.calculateLocalInertia(physics_body_get_mass(pb), inertia);
 	}
-	let body_ci: Ammo.btRigidBodyConstructionInfo = new Ammo.btRigidBodyConstructionInfo(pb.mass, pb.motion_state, pb.btshape, inertia);
+	let body_ci: Ammo.btRigidBodyConstructionInfo = new Ammo.btRigidBodyConstructionInfo(physics_body_get_mass(pb), pb.motion_state, pb.btshape, inertia);
 	pb.body = new Ammo.btRigidBody(body_ci);
 
 	pb.body.setFriction(pb.friction);
@@ -206,7 +224,7 @@ function physics_body_init(pb: PhysicsBodyRaw, o: object_t) {
 	if (pb.trigger) {
 		pb.body.setCollisionFlags(pb.body.getCollisionFlags() | collision_flags_t.CF_NO_CONTACT_RESPONSE);
 	}
-	if (pb.mass == 0.0) {
+	if (physics_body_get_mass(pb) == 0.0) {
 		pb.body.setCollisionFlags(pb.body.getCollisionFlags() | collision_flags_t.CF_STATIC_OBJECT);
 	}
 	if (pb.ccd) {
@@ -227,7 +245,7 @@ function physics_body_init(pb: PhysicsBodyRaw, o: object_t) {
 	Ammo.destroy(body_ci);
 }
 
-function physics_body_physics_update(pb: PhysicsBodyRaw) {
+function physics_body_physics_update(pb: physics_body_t) {
 	if (!pb.ready) {
 		return;
 	}
@@ -249,20 +267,20 @@ function physics_body_physics_update(pb: PhysicsBodyRaw) {
 	transform_build_matrix(transform);
 }
 
-function physics_body_remove_from_world(pb: PhysicsBodyRaw) {
+function physics_body_remove_from_world(pb: physics_body_t) {
 	physics_world_remove_body(physics_world_active, pb);
 }
 
-function physics_body_activate(pb: PhysicsBodyRaw) {
+function physics_body_activate(pb: physics_body_t) {
 	pb.body.activate(false);
 }
 
-function physics_body_set_gravity(pb: PhysicsBodyRaw, v: vec4_t) {
+function physics_body_set_gravity(pb: physics_body_t, v: vec4_t) {
 	physics_body_vec1.setValue(v.x, v.y, v.z);
 	pb.body.setGravity(physics_body_vec1);
 }
 
-function physics_body_apply_force(pb: PhysicsBodyRaw, force: vec4_t, loc: vec4_t = null) {
+function physics_body_apply_force(pb: physics_body_t, force: vec4_t, loc: vec4_t = null) {
 	physics_body_activate(pb);
 	physics_body_vec1.setValue(force.x, force.y, force.z);
 	if (loc == null) {
@@ -274,7 +292,7 @@ function physics_body_apply_force(pb: PhysicsBodyRaw, force: vec4_t, loc: vec4_t
 	}
 }
 
-function physics_body_apply_impulse(pb: PhysicsBodyRaw, impulse: vec4_t, loc: vec4_t = null) {
+function physics_body_apply_impulse(pb: physics_body_t, impulse: vec4_t, loc: vec4_t = null) {
 	physics_body_activate(pb);
 	physics_body_vec1.setValue(impulse.x, impulse.y, impulse.z);
 	if (loc == null) {
@@ -286,54 +304,54 @@ function physics_body_apply_impulse(pb: PhysicsBodyRaw, impulse: vec4_t, loc: ve
 	}
 }
 
-function physics_body_apply_torque(pb: PhysicsBodyRaw, torque: vec4_t) {
+function physics_body_apply_torque(pb: physics_body_t, torque: vec4_t) {
 	physics_body_activate(pb);
 	physics_body_vec1.setValue(torque.x, torque.y, torque.z);
 	pb.body.applyTorque(physics_body_vec1);
 }
 
-function physics_body_apply_torque_impulse(pb: PhysicsBodyRaw, torque: vec4_t) {
+function physics_body_apply_torque_impulse(pb: physics_body_t, torque: vec4_t) {
 	physics_body_activate(pb);
 	physics_body_vec1.setValue(torque.x, torque.y, torque.z);
 	pb.body.applyTorqueImpulse(physics_body_vec1);
 }
 
-function physics_body_set_linear_factor(pb: PhysicsBodyRaw, x: f32, y: f32, z: f32) {
+function physics_body_set_linear_factor(pb: physics_body_t, x: f32, y: f32, z: f32) {
 	physics_body_vec1.setValue(x, y, z);
 	pb.body.setLinearFactor(physics_body_vec1);
 }
 
-function physics_body_set_angular_factor(pb: PhysicsBodyRaw, x: f32, y: f32, z: f32) {
+function physics_body_set_angular_factor(pb: physics_body_t, x: f32, y: f32, z: f32) {
 	physics_body_vec1.setValue(x, y, z);
 	pb.body.setAngularFactor(physics_body_vec1);
 }
 
-function physics_body_get_linear_velocity(pb: PhysicsBodyRaw): vec4_t {
+function physics_body_get_linear_velocity(pb: physics_body_t): vec4_t {
 	let v: Ammo.btVector3 = pb.body.getLinearVelocity();
 	return vec4_create(v.x(), v.y(), v.z());
 }
 
-function physics_body_set_linear_velocity(pb: PhysicsBodyRaw, x: f32, y: f32, z: f32) {
+function physics_body_set_linear_velocity(pb: physics_body_t, x: f32, y: f32, z: f32) {
 	physics_body_vec1.setValue(x, y, z);
 	pb.body.setLinearVelocity(physics_body_vec1);
 }
 
-function physics_body_get_angular_velocity(pb: PhysicsBodyRaw): vec4_t {
+function physics_body_get_angular_velocity(pb: physics_body_t): vec4_t {
 	let v: Ammo.btVector3 = pb.body.getAngularVelocity();
 	return vec4_create(v.x(), v.y(), v.z());
 }
 
-function physics_body_set_angular_velocity(pb: PhysicsBodyRaw, x: f32, y: f32, z: f32) {
+function physics_body_set_angular_velocity(pb: physics_body_t, x: f32, y: f32, z: f32) {
 	physics_body_vec1.setValue(x, y, z);
 	pb.body.setAngularVelocity(physics_body_vec1);
 }
 
-function physics_body_set_friction(pb: PhysicsBodyRaw, f: f32) {
+function physics_body_set_friction(pb: physics_body_t, f: f32) {
 	pb.body.setFriction(f);
 	pb.friction = f;
 }
 
-function physics_body_set_scale(pb: PhysicsBodyRaw, v: vec4_t) {
+function physics_body_set_scale(pb: physics_body_t, v: vec4_t) {
 	pb.current_scale_x = v.x;
 	pb.current_scale_y = v.y;
 	pb.current_scale_z = v.z;
@@ -346,7 +364,7 @@ function physics_body_set_scale(pb: PhysicsBodyRaw, v: vec4_t) {
 	world_col.updateSingleAabb(pb.body);
 }
 
-function physics_body_sync_transform(pb: PhysicsBodyRaw) {
+function physics_body_sync_transform(pb: physics_body_t) {
 	let t: transform_t = pb.object.transform;
 	transform_build_matrix(t);
 	physics_body_vec1.setValue(transform_world_x(t), transform_world_y(t), transform_world_z(t));
@@ -361,12 +379,12 @@ function physics_body_sync_transform(pb: PhysicsBodyRaw) {
 	physics_body_activate(pb);
 }
 
-function physics_body_set_ccd(pb: PhysicsBodyRaw, sphereRadius: f32, motionThreshold = 1e-7) {
+function physics_body_set_ccd(pb: physics_body_t, sphereRadius: f32, motionThreshold = 1e-7) {
 	pb.body.setCcdSweptSphereRadius(sphereRadius);
 	pb.body.setCcdMotionThreshold(motionThreshold);
 }
 
-function physics_body_fill_convex_hull(pb: PhysicsBodyRaw, scale: vec4_t, margin: f32): Ammo.btConvexHullShape {
+function physics_body_fill_convex_hull(pb: physics_body_t, scale: vec4_t, margin: f32): Ammo.btConvexHullShape {
 	// Check whether shape already exists
 	let data: any = pb.object.ext.data;
 	let shape: Ammo.btConvexHullShape = map_get(physics_body_convex_hull_cache, data);
@@ -398,7 +416,7 @@ function physics_body_fill_convex_hull(pb: PhysicsBodyRaw, scale: vec4_t, margin
 	return shape;
 }
 
-function physics_body_fill_triangle_mesh(pb: PhysicsBodyRaw, scale: vec4_t): Ammo.btTriangleMesh {
+function physics_body_fill_triangle_mesh(pb: physics_body_t, scale: vec4_t): Ammo.btTriangleMesh {
 	// Check whether shape already exists
 	let data: any = pb.object.ext.data;
 	let triangle_mesh: Ammo.btTriangleMesh = map_get(physics_body_triangle_mesh_cache, data);
@@ -422,7 +440,8 @@ function physics_body_fill_triangle_mesh(pb: PhysicsBodyRaw, scale: vec4_t): Amm
 	sy *= data.scale_pos;
 	sz *= data.scale_pos;
 
-	for (let ar of indices) {
+	for (let i: i32 = 0; i < indices.length; ++i) {
+		let ar: any = indices[i];
 		for (let i: i32 = 0; i < math_floor(ar.length / 3); ++i) {
 			physics_body_vec1.setX(positions[ar[i * 3    ] * 4    ] * sx);
 			physics_body_vec1.setY(positions[ar[i * 3    ] * 4 + 1] * sy);
@@ -439,7 +458,7 @@ function physics_body_fill_triangle_mesh(pb: PhysicsBodyRaw, scale: vec4_t): Amm
 	return triangle_mesh;
 }
 
-function physics_body_delete(pb: PhysicsBodyRaw) {
+function physics_body_delete(pb: physics_body_t) {
 	Ammo.destroy(pb.motion_state);
 	Ammo.destroy(pb.body);
 

+ 40 - 31
base/Sources/physics_world.ts

@@ -1,17 +1,17 @@
 
 ///if arm_physics
 
-class PhysicsWorldRaw {
-	world: Ammo.btDiscreteDynamicsWorld;
-	dispatcher: Ammo.btCollisionDispatcher;
-	contacts: pair_t[] = [];
-	body_map: map_t<i32, PhysicsBodyRaw> = map_create();
-	time_scale: f32 = 1.0;
-	time_step: f32 = 1 / 60;
-	max_steps: i32 = 1;
-}
+type physics_world_t = {
+	world?: Ammo.btDiscreteDynamicsWorld;
+	dispatcher?: Ammo.btCollisionDispatcher;
+	contacts?: pair_t[];
+	body_map?: map_t<i32, physics_body_t>;
+	time_scale?: f32;
+	time_step?: f32;
+	max_steps?: i32;
+};
 
-let physics_world_active: PhysicsWorldRaw = null;
+let physics_world_active: physics_world_t = null;
 let physics_world_vec1: Ammo.btVector3 = null;
 let physics_world_vec2: Ammo.btVector3 = null;
 let physics_world_v1: vec4_t = vec4_create();
@@ -19,15 +19,20 @@ let physics_world_v2: vec4_t = vec4_create();
 
 function physics_world_load(done: ()=>void) {
 	let b: buffer_t = krom_load_blob("data/plugins/ammo.js");
-	globalThis.eval(sys_buffer_to_string(b));
+	js_eval(sys_buffer_to_string(b));
 	let print = function (s: string) {
 		krom_log(s);
 	};
 	Ammo({print: print}).then(done);
 }
 
-function physics_world_create(): PhysicsWorldRaw {
-	let pw: PhysicsWorldRaw = new PhysicsWorldRaw();
+function physics_world_create(): physics_world_t {
+	let pw: physics_world_t = {};
+	pw.contacts = [];
+	pw.body_map = map_create();
+	pw.time_scale = 1.0;
+	pw.time_step = 1 / 60;
+	pw.max_steps = 1;
 	physics_world_active = pw;
 	physics_world_vec1 = new Ammo.btVector3(0, 0, 0);
 	physics_world_vec2 = new Ammo.btVector3(0, 0, 0);
@@ -35,13 +40,15 @@ function physics_world_create(): PhysicsWorldRaw {
 	return pw;
 }
 
-function physics_world_reset(pw: PhysicsWorldRaw) {
-	for (let body of pw.body_map.values()) {
+function physics_world_reset(pw: physics_world_t) {
+	let values: physics_body_t[] = map_to_array(pw.body_map);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let body: physics_body_t = values[i];
 		physics_world_remove_body(pw, body);
 	}
 }
 
-function physics_world_init(pw: PhysicsWorldRaw) {
+function physics_world_init(pw: physics_world_t) {
 	let broadphase: Ammo.btDbvtBroadphase = new Ammo.btDbvtBroadphase();
 	let collision_conf: Ammo.btDefaultCollisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
 	pw.dispatcher = new Ammo.btCollisionDispatcher(collision_conf);
@@ -50,17 +57,17 @@ function physics_world_init(pw: PhysicsWorldRaw) {
 	physics_world_set_gravity(pw, vec4_create(0, 0, -9.81));
 }
 
-function physics_world_set_gravity(pw: PhysicsWorldRaw, v: vec4_t) {
+function physics_world_set_gravity(pw: physics_world_t, v: vec4_t) {
 	physics_world_vec1.setValue(v.x, v.y, v.z);
 	pw.world.setGravity(physics_world_vec1);
 }
 
-function physics_world_add_body(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw) {
+function physics_world_add_body(pw: physics_world_t, pb: physics_body_t) {
 	pw.world.addRigidBody(pb.body, pb.group, pb.mask);
 	map_set(pw.body_map, pb.id, pb);
 }
 
-function physics_world_remove_body(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw) {
+function physics_world_remove_body(pw: physics_world_t, pb: physics_body_t) {
 	if (pb.destroyed) {
 		return;
 	}
@@ -72,14 +79,14 @@ function physics_world_remove_body(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw) {
 	physics_body_delete(pb);
 }
 
-function physics_world_get_contacts(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw): PhysicsBodyRaw[] {
+function physics_world_get_contacts(pw: physics_world_t, pb: physics_body_t): physics_body_t[] {
 	if (pw.contacts.length == 0) {
 		return null;
 	}
-	let res: PhysicsBodyRaw[] = [];
+	let res: physics_body_t[] = [];
 	for (let i: i32 = 0; i < pw.contacts.length; ++i) {
 		let c: pair_t = pw.contacts[i];
-		let pb: PhysicsBodyRaw = null;
+		let pb: physics_body_t = null;
 		if (c.a == pb.body.userIndex) {
 			pb = map_get(pw.body_map, c.b);
 		}
@@ -93,7 +100,7 @@ function physics_world_get_contacts(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw): Ph
 	return res;
 }
 
-function physics_world_get_contact_pairs(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw): pair_t[] {
+function physics_world_get_contact_pairs(pw: physics_world_t, pb: physics_body_t): pair_t[] {
 	if (pw.contacts.length == 0) {
 		return null;
 	}
@@ -110,7 +117,7 @@ function physics_world_get_contact_pairs(pw: PhysicsWorldRaw, pb: PhysicsBodyRaw
 	return res;
 }
 
-function physics_world_late_update(pw: PhysicsWorldRaw) {
+function physics_world_late_update(pw: physics_world_t) {
 	let t: f32 = time_delta() * pw.time_scale;
 	if (t == 0.0) {
 		return; // Simulation paused
@@ -118,12 +125,14 @@ function physics_world_late_update(pw: PhysicsWorldRaw) {
 
 	pw.world.stepSimulation(pw.time_step, pw.max_steps, t);
 	physics_world_update_contacts(pw);
-	for (let body of pw.body_map.values()) {
+	let values: physics_body_t[] = map_to_array(pw.body_map);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let body: physics_body_t = values[i];
 		physics_body_physics_update(body);
 	}
 }
 
-function physics_world_update_contacts(pw: PhysicsWorldRaw) {
+function physics_world_update_contacts(pw: physics_world_t) {
 	pw.contacts = [];
 	let disp: Ammo.btDispatcher = pw.dispatcher;
 	let num_manifolds: i32 = disp.getNumManifolds();
@@ -157,17 +166,17 @@ function physics_world_update_contacts(pw: PhysicsWorldRaw) {
 	}
 }
 
-function physics_world_pick_closest(pw: PhysicsWorldRaw, inputX: f32, inputY: f32): PhysicsBodyRaw {
+function physics_world_pick_closest(pw: physics_world_t, inputX: f32, inputY: f32): physics_body_t {
 	let camera: camera_object_t = scene_camera;
 	let start: vec4_t = vec4_create();
 	let end: vec4_t = vec4_create();
 	raycast_get_dir(start, end, inputX, inputY, camera);
 	let hit: hit_t = physics_world_ray_cast(pw, mat4_get_loc(camera.base.transform.world), end);
-	let body: PhysicsBodyRaw = (hit != null) ? hit.body : null;
+	let body: physics_body_t = (hit != null) ? hit.body : null;
 	return body;
 }
 
-function physics_world_ray_cast(pw: PhysicsWorldRaw, from: vec4_t, to: vec4_t, group: i32 = 0x00000001, mask: i32 = 0xffffffff): hit_t {
+function physics_world_ray_cast(pw: physics_world_t, from: vec4_t, to: vec4_t, group: i32 = 0x00000001, mask: i32 = 0xffffffff): hit_t {
 	let ray_from: Ammo.btVector3 = physics_world_vec1;
 	let ray_to: Ammo.btVector3 = physics_world_vec2;
 	ray_from.setValue(from.x, from.y, from.z);
@@ -181,7 +190,7 @@ function physics_world_ray_cast(pw: PhysicsWorldRaw, from: vec4_t, to: vec4_t, g
 	let world_dyn: Ammo.btDynamicsWorld = pw.world;
 	let world_col: Ammo.btCollisionWorld = world_dyn;
 	world_col.rayTest(ray_from, ray_to, ray_callback);
-	let pb: PhysicsBodyRaw = null;
+	let pb: physics_body_t = null;
 	let hit_info: hit_t = null;
 
 	let rc: Ammo.RayResultCallback = ray_callback;
@@ -205,7 +214,7 @@ function physics_world_ray_cast(pw: PhysicsWorldRaw, from: vec4_t, to: vec4_t, g
 }
 
 type hit_t = {
-	body?: PhysicsBodyRaw;
+	body?: physics_body_t;
 	pos?: vec4_t;
 	normal?: vec4_t;
 };

+ 16 - 23
base/Sources/plugin.ts

@@ -1,39 +1,32 @@
 
-class PluginRaw {
-	draw_ui: (ui: zui_t)=>void = null;
-	draw: ()=>void = null;
-	update: ()=>void = null;
-	delete: ()=>void = null;
-	version: string = "0.1";
-	name: string;
-}
+type plugin_t = {
+	draw_ui?: (ui: zui_t)=>void;
+	draw?: ()=>void;
+	update?: ()=>void;
+	delete?: ()=>void;
+	version?: string;
+	name?: string;
+};
 
-let plugin_map: map_t<string, PluginRaw> = map_create();
+let plugin_map: map_t<string, plugin_t> = map_create();
 let _plugin_name: string;
 
-function plugin_create(): PluginRaw {
-	let p: PluginRaw = new PluginRaw();
+function plugin_create(): plugin_t {
+	let p: plugin_t = {};
 	p.name = _plugin_name;
 	map_set(plugin_map, p.name, p);
 	return p;
 }
 
 function plugin_start(plugin: string) {
-	try {
-		let blob: buffer_t = data_get_blob("plugins/" + plugin);
-		_plugin_name = plugin;
-		// (1, eval)(sys_buffer_to_string(blob)); // Global scope
-		eval(sys_buffer_to_string(blob)); // Local scope
-		data_delete_blob("plugins/" + plugin);
-	}
-	catch (e: any) {
-		console_error(tr("Failed to load plugin") + " '" + plugin + "'");
-		krom_log(e);
-	}
+	let blob: buffer_t = data_get_blob("plugins/" + plugin);
+	_plugin_name = plugin;
+	js_eval(sys_buffer_to_string(blob), plugin);
+	data_delete_blob("plugins/" + plugin);
 }
 
 function plugin_stop(plugin: string) {
-	let p: PluginRaw = map_get(plugin_map, plugin);
+	let p: plugin_t = map_get(plugin_map, plugin);
 	if (p != null && p.delete != null) {
 		p.delete();
 	}

+ 27 - 15
base/Sources/project.ts

@@ -266,7 +266,8 @@ function project_new(resetLayers: bool = true) {
 	util_render_make_material_preview();
 	///end
 
-	for (let a of project_assets) {
+	for (let i: i32 = 0; i < project_assets.length; ++i) {
+		let a: asset_t = project_assets[i];
 		data_delete_image(a.file);
 	}
 	project_assets = [];
@@ -473,7 +474,8 @@ function project_unwrap_mesh_box(mesh: any, done: (a: any)=>void, skip_ui: bool
 			if (box_preferences_files_plugin == null) {
 				box_preferences_fetch_plugins();
 			}
-			for (let f of box_preferences_files_plugin) {
+			for (let i: i32 = 0; i < box_preferences_files_plugin.length; ++i) {
+				let f: string = box_preferences_files_plugin[i];
 				if (string_index_of(f, "uv_unwrap") >= 0 && ends_with(f, ".js")) {
 					array_push(unwrap_plugins, f);
 				}
@@ -536,7 +538,8 @@ function project_import_swatches(replaceExisting: bool = false) {
 }
 
 function project_reimport_textures() {
-	for (let asset of project_assets) {
+	for (let i: i32 = 0; i < project_assets.length; ++i) {
+		let asset: asset_t = project_assets[i];
 		project_reimport_texture(asset);
 	}
 }
@@ -587,15 +590,17 @@ function project_get_image(asset: asset_t): image_t {
 function project_get_used_atlases(): string[] {
 	if (project_atlas_objects == null) return null;
 	let used: i32[] = [];
-	for (let i of project_atlas_objects) {
-		if (array_index_of(used, i) == -1) {
-			array_push(used, i);
+	for (let i: i32 = 0; i < project_atlas_objects.length; ++i) {
+		let ao: i32 = project_atlas_objects[i];
+		if (array_index_of(used, ao) == -1) {
+			array_push(used, ao);
 		}
 	}
 	if (used.length > 1) {
 		let res: string[] = [];
-		for (let i of used) {
-			array_push(res, project_atlas_names[i]);
+		for (let i: i32 = 0; i < used.length; ++i) {
+			let u: i32 = used[i];
+			array_push(res, project_atlas_names[u]);
 		}
 		return res;
 	}
@@ -625,7 +630,8 @@ function project_get_atlas_objects(objectMask: i32): mesh_object_t[] {
 ///end
 
 function project_packed_asset_exists(packed_assets: packed_asset_t[], name: string): bool {
-	for (let pa of packed_assets) {
+	for (let i: i32 = 0; i < packed_assets.length; ++i) {
+		let pa: packed_asset_t = packed_assets[i];
 		if (pa.name == name) {
 			return true;
 		}
@@ -661,13 +667,15 @@ function project_set_default_swatches() {
 	// http://eastfarthing.com/blog/2016-05-06-palette/
 	project_raw.swatches = [];
 	let colors: i32[] = [0xffffffff, 0xff000000, 0xffd6a090, 0xffa12c32, 0xfffa2f7a, 0xfffb9fda, 0xffe61cf7, 0xff992f7c, 0xff47011f, 0xff051155, 0xff4f02ec, 0xff2d69cb, 0xff00a6ee, 0xff6febff, 0xff08a29a, 0xff2a666a, 0xff063619, 0xff4a4957, 0xff8e7ba4, 0xffb7c0ff, 0xffacbe9c, 0xff827c70, 0xff5a3b1c, 0xffae6507, 0xfff7aa30, 0xfff4ea5c, 0xff9b9500, 0xff566204, 0xff11963b, 0xff51e113, 0xff08fdcc];
-	for (let c of colors) {
+	for (let i: i32 = 0; i < colors.length; ++i) {
+		let c: i32 = colors[i];
 		array_push(project_raw.swatches, make_swatch(c));
 	}
 }
 
 function project_get_material_group_by_name(groupName: string): node_group_t {
-	for (let g of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let g: node_group_t = project_material_groups[i];
 		if (g.canvas.name == groupName) {
 			return g;
 		}
@@ -678,14 +686,18 @@ function project_get_material_group_by_name(groupName: string): node_group_t {
 ///if (is_paint || is_sculpt)
 function project_is_material_group_in_use(group: node_group_t): bool {
 	let canvases: zui_node_canvas_t[] = [];
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		array_push(canvases, m.canvas);
 	}
-	for (let m of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let m: node_group_t = project_material_groups[i];
 		array_push(canvases, m.canvas);
 	}
-	for (let canvas of canvases) {
-		for (let n of canvas.nodes) {
+	for (let i: i32 = 0; i < canvases.length; ++i) {
+		let canvas: zui_node_canvas_t = canvases[i];
+		for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+			let n: zui_node_t = canvas.nodes[i];
 			if (n.type == "GROUP" && n.name == group.canvas.name) {
 				return true;
 			}

+ 3 - 1
base/Sources/render_path_base.ts

@@ -40,7 +40,9 @@ function render_path_base_init_voxels(target_name: string = "voxels") {
 function render_path_base_apply_config() {
 	if (render_path_base_super_sample != config_raw.rp_supersample) {
 		render_path_base_super_sample = config_raw.rp_supersample;
-		for (let rt of render_path_render_targets.values()) {
+		let values: render_target_t[] = map_to_array(render_path_render_targets);
+		for (let i: i32 = 0; i < values.length; ++i) {
+			let rt: render_target_t = values[i];
 			if (rt.width == 0 && rt.scale != null) {
 				rt.scale = render_path_base_super_sample;
 			}

+ 2 - 1
base/Sources/resource.ts

@@ -2,7 +2,8 @@
 let resource_bundled: map_t<string, image_t> = map_create();
 
 function resource_load(names: string[]) {
-	for (let s of names) {
+	for (let i: i32 = 0; i < names.length; ++i) {
+		let s: string = names[i];
 		let image: image_t = data_get_image(s);
 		map_set(resource_bundled, s, image);
 	}

+ 2 - 1
base/Sources/tab_browser.ts

@@ -205,7 +205,8 @@ function tab_browser_draw(htab: zui_handle_t) {
 			///end
 		}
 
-		for (let b of config_raw.bookmarks) {
+		for (let i: i32 = 0; i < config_raw.bookmarks.length; ++i) {
+			let b: string = config_raw.bookmarks[i];
 			let folder: string = substring(b, string_last_index_of(b, path_sep) + 1, b.length);
 
 			if (zui_button(folder, zui_align_t.LEFT)) {

+ 2 - 1
base/Sources/tab_console.ts

@@ -56,7 +56,8 @@ function tab_console_draw(htab: zui_handle_t) {
 		let f: g2_font_t = data_get_font("font_mono.ttf");
 		zui_set_font(ui, f);
 		ui.font_size = math_floor(15 * zui_SCALE(ui));
-		for (let t of console_last_traces) {
+		for (let i: i32 = 0; i < console_last_traces.length; ++i) {
+			let t: string = console_last_traces[i];
 			zui_text(t);
 		}
 		zui_set_font(ui, _font);

+ 8 - 4
base/Sources/tab_materials.ts

@@ -301,7 +301,8 @@ function tab_materials_update_material() {
 }
 
 function tab_materials_update_material_pointers(nodes: zui_node_t[], i: i32) {
-	for (let n of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let n: zui_node_t = nodes[i];
 		if (n.type == "MATERIAL") {
 			if (n.buttons[0].default_value == i) {
 				n.buttons[0].default_value = 9999; // Material deleted
@@ -315,7 +316,8 @@ function tab_materials_update_material_pointers(nodes: zui_node_t[], i: i32) {
 
 function tab_materials_accept_swatch_drag(swatch: swatch_color_t) {
 	context_raw.material = slot_material_create(project_materials[0].data);
-	for (let node of context_raw.material.canvas.nodes) {
+	for (let i: i32 = 0; i < context_raw.material.canvas.nodes.length; ++i) {
+		let node: zui_node_t = context_raw.material.canvas.nodes[i];
 		if (node.type == "RGB" ) {
 			node.outputs[0].default_value = [
 				color_get_rb(swatch.base) / 255,
@@ -339,7 +341,8 @@ function tab_materials_accept_swatch_drag(swatch: swatch_color_t) {
 
 function tab_materials_delete_material(m: slot_material_t) {
 	let i: i32 = array_index_of(project_materials, m);
-	for (let l of project_layers) {
+	for (let i: i32 = 0; i < project_layers.length; ++i) {
+		let l: slot_layer_t = project_layers[i];
 		if (l.fill_layer == m) {
 			l.fill_layer = null;
 		}
@@ -348,7 +351,8 @@ function tab_materials_delete_material(m: slot_material_t) {
 	context_select_material(i == project_materials.length - 1 ? i - 1 : i + 1);
 	array_splice(project_materials, i, 1);
 	ui_base_hwnds[1].redraws = 2;
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		tab_materials_update_material_pointers(m.canvas.nodes, i);
 	}
 }

+ 2 - 1
base/Sources/tab_meshes.ts

@@ -147,7 +147,8 @@ function tab_meshes_draw(htab: zui_handle_t) {
 			}
 			if (h.changed) {
 				let visibles: mesh_object_t[] = [];
-				for (let p of project_paint_objects) {
+				for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+					let p: mesh_object_t = project_paint_objects[i];
 					if (p.base.visible) {
 						array_push(visibles, p);
 					}

+ 3 - 1
base/Sources/tab_plugins.ts

@@ -19,7 +19,9 @@ function tab_plugins_draw(htab: zui_handle_t) {
 		zui_end_sticky();
 
 		// Draw plugins
-		for (let p of plugin_map.values()) {
+		let values: plugin_t[] = map_to_array(plugin_map);
+		for (let i: i32 = 0; i < values.length; ++i) {
+			let p: plugin_t = values[i];
 			if (p.draw_ui != null) {
 				p.draw_ui(ui);
 			}

+ 3 - 7
base/Sources/tab_script.ts

@@ -15,12 +15,7 @@ function tab_script_draw(htab: zui_handle_t) {
 			zui_row([1 / 14, 1 / 14, 1 / 14, 1 / 14]);
 		}
 		if (zui_button(tr("Run"))) {
-			try {
-				eval(tab_script_hscript.text);
-			}
-			catch(e: any) {
-				console_log(e);
-			}
+			js_eval(tab_script_hscript.text);
 		}
 		if (zui_button(tr("Clear"))) {
 			tab_script_hscript.text = "";
@@ -70,7 +65,8 @@ function tab_script_get_text_coloring(): zui_text_coloring_t {
 		let blob: buffer_t = data_get_blob("text_coloring.json");
 		tab_script_text_coloring = json_parse(sys_buffer_to_string(blob));
 		tab_script_text_coloring.default_color = math_floor(tab_script_text_coloring.default_color);
-		for (let coloring of tab_script_text_coloring.colorings) {
+		for (let i: i32 = 0; i < tab_script_text_coloring.colorings.length; ++i) {
+			let coloring: zui_coloring_t = tab_script_text_coloring.colorings[i];
 			coloring.color = math_floor(coloring.color);
 		}
 	}

+ 6 - 3
base/Sources/tab_textures.ts

@@ -236,7 +236,8 @@ function tab_textures_to_pow2(i: i32): i32 {
 }
 
 function tab_textures_update_texture_pointers(nodes: zui_node_t[], i: i32) {
-	for (let n of nodes) {
+	for (let i: i32 = 0; i < nodes.length; ++i) {
+		let n: zui_node_t = nodes[i];
 		if (n.type == "TEX_IMAGE") {
 			if (n.buttons[0].default_value == i) {
 				n.buttons[0].default_value = 9999; // Texture deleted, use pink now
@@ -278,11 +279,13 @@ function tab_textures_delete_texture(asset: asset_t) {
 	}
 	base_notify_on_next_frame(_next);
 
-	for (let m of project_materials) {
+	for (let i: i32 = 0; i < project_materials.length; ++i) {
+		let m: slot_material_t = project_materials[i];
 		tab_textures_update_texture_pointers(m.canvas.nodes, i);
 	}
 	///if (is_paint || is_sculpt)
-	for (let b of project_brushes) {
+	for (let i: i32 = 0; i < project_brushes.length; ++i) {
+		let b: slot_brush_t = project_brushes[i];
 		tab_textures_update_texture_pointers(b.canvas.nodes, i);
 	}
 	///end

+ 17 - 10
base/Sources/translator.ts

@@ -1,13 +1,7 @@
 
 let translator_translations: map_t<string, string> = map_create();
-// The font index is a value specific to font_cjk.ttc.
-let translator_cjk_font_indices: map_t<string, i32> = new map_t([
-	["ja", 0],
-	["ko", 1],
-	["zh_cn", 2],
-	["zh_tw", 3],
-	["zh_tw.big5", 4]
-]);
+// The font index is a value specific to font_cjk.ttc
+let translator_cjk_font_indices: map_t<string, i32> = null;
 
 let translator_last_locale: string = "en";
 
@@ -39,6 +33,16 @@ function tr(id: string, vars: map_t<string, string> = null): string {
 
 // (Re)loads translations for the specified locale
 function translator_load_translations(new_locale: string) {
+
+	if (translator_cjk_font_indices == null) {
+		translator_cjk_font_indices = map_create();
+		map_set(translator_cjk_font_indices, "ja", 0);
+		map_set(translator_cjk_font_indices, "ko", 1);
+		map_set(translator_cjk_font_indices, "zh_cn", 2);
+		map_set(translator_cjk_font_indices, "zh_tw", 3);
+		map_set(translator_cjk_font_indices, "zh_tw.big5", 4);
+	}
+
 	if (new_locale == "system") {
 		config_raw.locale = krom_language();
 	}
@@ -75,7 +79,9 @@ function translator_load_translations(new_locale: string) {
 
 	// Push additional char codes contained in translation file
 	let cjk: bool = false;
-	for (let s of translator_translations.values()) {
+	let values: string[] = map_to_array(translator_translations);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let s: string = values[i];
 		for (let i: i32 = 0; i < s.length; ++i) {
 			// Assume cjk in the > 1119 range for now
 			if (char_code_at(s, i) > 1119 && array_index_of(_g2_font_glyphs, char_code_at(s, i)) == -1) {
@@ -131,7 +137,8 @@ function translator_init_font(cjk: bool, font_path: string, font_scale: f32) {
 		base_theme.FONT_SIZE = math_floor(base_default_font_size * font_scale);
 		base_theme.ELEMENT_W = math_floor(base_default_element_w * (config_raw.locale != "en" ? 1.4 : 1.0));
 		let uis: zui_t[] = base_get_uis();
-		for (let ui of uis) {
+		for (let i: i32 = 0; i < uis.length; ++i) {
+			let ui: zui_t = uis[i];
 			zui_set_font(ui, f);
 			zui_set_scale(ui, zui_SCALE(ui));
 		}

+ 12 - 7
base/Sources/ui_base.ts

@@ -216,7 +216,8 @@ function ui_base_init() {
 	}
 
 	context_raw.project_objects = [];
-	for (let m of scene_meshes) {
+	for (let i: i32 = 0; i < scene_meshes.length; ++i) {
+		let m: mesh_object_t = scene_meshes[i];
 		array_push(context_raw.project_objects, m);
 	}
 
@@ -227,7 +228,9 @@ function ui_base_update() {
 	ui_base_update_ui();
 	operator_update();
 
-	for (let p of plugin_map.values()) {
+	let values: plugin_t[] = map_to_array(plugin_map);
+	for (let i: i32 = 0; i < values.length; ++i) {
+		let p: plugin_t = values[i];
 		if (p.update != null) {
 			p.update();
 		}
@@ -783,7 +786,7 @@ function ui_base_update() {
 	///if arm_physics
 	if (context_raw.tool == workspace_tool_t.PARTICLE && context_raw.particle_physics && context_in_paint_area() && !context_raw.paint2d) {
 		util_particle_init_physics();
-		let world: PhysicsWorldRaw = physics_world_active;
+		let world: physics_world_t = physics_world_active;
 		physics_world_late_update(world);
 		context_raw.ddirty = 2;
 		context_raw.rdirty = 2;
@@ -808,9 +811,9 @@ function ui_base_update() {
 			vec4_set(mo.base.transform.scale, context_raw.brush_radius * 0.2, context_raw.brush_radius * 0.2, context_raw.brush_radius * 0.2);
 			transform_build_matrix(mo.base.transform);
 
-			let body: PhysicsBodyRaw = physics_body_create();
+			let body: physics_body_t = physics_body_create();
 			body.shape = shape_type_t.SPHERE;
-			body.mass = 1.0;
+			physics_body_set_mass(body, 1.0);
 			body.ccd = true;
 			mo.base.transform.radius /= 10; // Lower ccd radius
 			physics_body_init(body, mo.base);
@@ -825,7 +828,8 @@ function ui_base_update() {
 
 		let pairs: pair_t[] = physics_world_get_contact_pairs(world, context_raw.paint_body);
 		if (pairs != null) {
-			for (let p of pairs) {
+			for (let i: i32 = 0; i < pairs.length; ++i) {
+				let p: pair_t = pairs[i];
 				context_raw.last_particle_hit_x = context_raw.particle_hit_x != 0 ? context_raw.particle_hit_x : p.pos_a.x;
 				context_raw.last_particle_hit_y = context_raw.particle_hit_y != 0 ? context_raw.particle_hit_y : p.pos_a.y;
 				context_raw.last_particle_hit_z = context_raw.particle_hit_z != 0 ? context_raw.particle_hit_z : p.pos_a.z;
@@ -1177,7 +1181,8 @@ function ui_base_update_ui() {
 			base_make_pipe();
 		}
 		// Update all layer previews
-		for (let l of project_layers) {
+		for (let i: i32 = 0; i < project_layers.length; ++i) {
+			let l: slot_layer_t = project_layers[i];
 			if (slot_layer_is_group(l)) {
 				continue;
 			}

+ 4 - 2
base/Sources/ui_files.ts

@@ -37,7 +37,8 @@ function ui_files_show(filters: string, is_save: bool, open_multiple: bool, file
 	else {
 		let paths: string[] = krom_open_dialog(filters, "", open_multiple);
 		if (paths != null) {
-			for (let path of paths) {
+			for (let i: i32 = 0; i < paths.length; ++i) {
+				let path: string = paths[i];
 				while (string_index_of(path, path_sep + path_sep) >= 0) {
 					path = string_replace_all(path, path_sep + path_sep, path_sep);
 				}
@@ -104,7 +105,8 @@ function ui_files_file_browser(ui: zui_t, handle: zui_handle_t, folders_only: bo
 		///end
 		let files_all: string[] = file_read_directory(dir_path, folders_only);
 
-		for (let f of files_all) {
+		for (let i: i32 = 0; i < files_all.length; ++i) {
+			let f: string = files_all[i];
 			if (f == "" || char_at(f, 0) == ".") {
 				continue; // Skip hidden
 			}

+ 30 - 8
base/Sources/ui_header.ts

@@ -67,7 +67,8 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 				import_asset_run(path, -1.0, -1.0, true, false);
 
 				context_raw.colorid_handle.position = project_asset_names.length - 1;
-				for (let a of project_assets) {
+				for (let i: i32 = 0; i < project_assets.length; ++i) {
+					let a: asset_t = project_assets[i];
 					// Already imported
 					if (a.file == path) {
 						context_raw.colorid_handle.position = array_index_of(project_assets, a);
@@ -285,7 +286,8 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 		}
 		if (context_raw.bake_type == bake_type_t.NORMAL || context_raw.bake_type == bake_type_t.HEIGHT || context_raw.bake_type == bake_type_t.DERIVATIVE) {
 			let ar: string[] = [];
-			for (let p of project_paint_objects) {
+			for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+				let p: mesh_object_t = project_paint_objects[i];
 				array_push(ar, p.base.name);
 			}
 			let poly_handle: zui_handle_t = zui_handle("uiheader_13", { position: context_raw.bake_high_poly });
@@ -311,13 +313,21 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 			if (decal_mask) {
 				context_raw.brush_decal_mask_radius = zui_slider(context_raw.brush_decal_mask_radius_handle, tr("Radius"), 0.01, 2.0, true);
 				if (ui.is_hovered) {
-					zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", new map_t([["brush_radius", config_keymap.brush_radius], ["brush_radius_decrease", config_keymap.brush_radius_decrease], ["brush_radius_increase", config_keymap.brush_radius_increase]])));
+					let vars: map_t<string, string> = map_create();
+					map_set(vars, "brush_radius", config_keymap.brush_radius);
+					map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
+					map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+					zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 				}
 			}
 			else {
 				context_raw.brush_radius = zui_slider(context_raw.brush_radius_handle, tr("Radius"), 0.01, 2.0, true);
 				if (ui.is_hovered) {
-					zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", new map_t([["brush_radius", config_keymap.brush_radius], ["brush_radius_decrease", config_keymap.brush_radius_decrease], ["brush_radius_increase", config_keymap.brush_radius_increase]])));
+					let vars: map_t<string, string> = map_create();
+					map_set(vars, "brush_radius", config_keymap.brush_radius);
+					map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
+					map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+					zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 				}
 			}
 		}
@@ -343,7 +353,9 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 
 			context_raw.brush_angle = zui_slider(context_raw.brush_angle_handle, tr("Angle"), 0.0, 360.0, true, 1);
 			if (ui.is_hovered) {
-				zui_tooltip(tr("Hold {brush_angle} and move mouse to the left to decrease the angle\nHold {brush_angle} and move mouse to the right to increase the angle", new map_t([["brush_angle", config_keymap.brush_angle]])));
+				let vars: map_t<string, string> = map_create();
+				map_set(vars, "brush_angle", config_keymap.brush_angle);
+				zui_tooltip(tr("Hold {brush_angle} and move mouse to the left to decrease the angle\nHold {brush_angle} and move mouse to the right to increase the angle", vars));
 			}
 
 			if (context_raw.brush_angle_handle.changed) {
@@ -353,7 +365,9 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 
 		context_raw.brush_opacity = zui_slider(context_raw.brush_opacity_handle, tr("Opacity"), 0.0, 1.0, true);
 		if (ui.is_hovered) {
-			zui_tooltip(tr("Hold {brush_opacity} and move mouse to the left to decrease the opacity\nHold {brush_opacity} and move mouse to the right to increase the opacity", new map_t([["brush_opacity", config_keymap.brush_opacity]])));
+			let vars: map_t<string, string> = map_create();
+			map_set(vars, "brush_opacity", config_keymap.brush_opacity);
+			zui_tooltip(tr("Hold {brush_opacity} and move mouse to the left to decrease the opacity\nHold {brush_opacity} and move mouse to the right to increase the opacity", vars));
 		}
 
 		if (context_raw.tool == workspace_tool_t.BRUSH || context_raw.tool == workspace_tool_t.ERASER || context_raw.tool == workspace_tool_t.CLONE || decal_mask) {
@@ -504,7 +518,11 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 	if (context_raw.tool == workspace_tool_t.BRUSH) {
 		context_raw.brush_radius = zui_slider(context_raw.brush_radius_handle, tr("Radius"), 0.01, 2.0, true);
 		if (ui.is_hovered) {
-			zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", new map_t([["brush_radius", config_keymap.brush_radius], ["brush_radius_decrease", config_keymap.brush_radius_decrease], ["brush_radius_increase", config_keymap.brush_radius_increase]])));
+			let vars: map_t<string, string> = map_create();
+			map_set(vars, "brush_radius", config_keymap.brush_radius);
+			map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
+			map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+			zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 		}
 	}
 }
@@ -526,7 +544,11 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 		if (inpaint) {
 			context_raw.brush_radius = zui_slider(context_raw.brush_radius_handle, tr("Radius"), 0.01, 2.0, true);
 			if (ui.is_hovered) {
-				zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", new map_t([["brush_radius", config_keymap.brush_radius], ["brush_radius_decrease", config_keymap.brush_radius_decrease], ["brush_radius_increase", config_keymap.brush_radius_increase]])));
+				let vars: map_t<string, string> = map_create();
+				map_set(vars, "brush_radius", config_keymap.brush_radius);
+				map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
+				map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+				zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 			}
 		}
 	}

+ 29 - 16
base/Sources/ui_menu.ts

@@ -127,12 +127,18 @@ function ui_menu_render() {
 			if (history_redos > 0) {
 				step_redo = history_steps[history_steps.length - history_redos].name;
 			}
+
 			ui.enabled = history_undos > 0;
-			if (ui_menu_button(ui, tr("Undo {step}", new map_t([["step", step_undo]])), config_keymap.edit_undo)) {
+			let vars_undo: map_t<string, string> = map_create();
+			map_set(vars_undo, "step", step_undo);
+			if (ui_menu_button(ui, tr("Undo {step}", vars_undo), config_keymap.edit_undo)) {
 				history_undo();
 			}
+
 			ui.enabled = history_redos > 0;
-			if (ui_menu_button(ui, tr("Redo {step}", new map_t([["step", step_redo]])), config_keymap.edit_redo)) {
+			let vars_redo: map_t<string, string> = map_create();
+			map_set(vars_redo, "step", step_redo);
+			if (ui_menu_button(ui, tr("Redo {step}", vars_redo), config_keymap.edit_redo)) {
 				history_redo();
 			}
 			ui.enabled = true;
@@ -177,7 +183,9 @@ function ui_menu_render() {
 			ui_menu_align(ui);
 			context_raw.envmap_angle = zui_slider(enva_handle, tr("Environment Angle"), 0.0, 360.0, true, 1) / 180.0 * math_pi();
 			if (ui.is_hovered) {
-				zui_tooltip(tr("{shortcut} and move mouse", new map_t([["shortcut", config_keymap.rotate_envmap]])));
+				let vars: map_t<string, string> = map_create();
+				map_set(vars, "shortcut", config_keymap.rotate_envmap);
+				zui_tooltip(tr("{shortcut} and move mouse", vars));
 			}
 			if (enva_handle.changed) {
 				context_raw.ddirty = 2;
@@ -204,7 +212,9 @@ function ui_menu_render() {
 				ui_menu_align(ui);
 				let new_angle: f32 = zui_slider(lahandle, tr("Light Angle"), 0.0, 360.0, true, 1) / 180 * math_pi();
 				if (ui.is_hovered) {
-					zui_tooltip(tr("{shortcut} and move mouse", new map_t([["shortcut", config_keymap.rotate_light]])));
+					let vars: map_t<string, string> = map_create();
+					map_set(vars, "shortcut", config_keymap.rotate_light);
+					zui_tooltip(tr("{shortcut} and move mouse", vars));
 				}
 				let ldiff: f32 = new_angle - context_raw.light_angle;
 				if (math_abs(ldiff) > 0.005) {
@@ -431,13 +441,11 @@ function ui_menu_render() {
 			camera_controls_handle.position = context_raw.camera_controls;
 			context_raw.camera_controls = zui_inline_radio(camera_controls_handle, [tr("Orbit"), tr("Rotate"), tr("Fly")], zui_align_t.LEFT);
 
-			let orbit_and_rotate_tooltip: string = tr("Orbit and Rotate mode:\n{rotate_shortcut} or move right mouse button to rotate.\n{zoom_shortcut} or scroll to zoom.\n{pan_shortcut} or move middle mouse to pan.",
-				new map_t([
-					["rotate_shortcut", config_keymap.action_rotate],
-					["zoom_shortcut", config_keymap.action_zoom],
-					["pan_shortcut", config_keymap.action_pan]
-				])
-			);
+			let vars: map_t<string, string> = map_create();
+			map_set(vars, "rotate_shortcut", config_keymap.action_rotate);
+			map_set(vars, "zoom_shortcut", config_keymap.action_zoom);
+			map_set(vars, "pan_shortcut", config_keymap.action_pan);
+			let orbit_and_rotate_tooltip: string = tr("Orbit and Rotate mode:\n{rotate_shortcut} or move right mouse button to rotate.\n{zoom_shortcut} or scroll to zoom.\n{pan_shortcut} or move middle mouse to pan.", vars);
 			let fly_tooltip: string = tr("Fly mode:\nHold the right mouse button and one of the following commands:\nmove mouse to rotate.\nw, up or scroll up to move forward.\ns, down or scroll down to move backward.\na or left to move left.\nd or right to move right.\ne to move up.\nq to move down.\nHold shift to move faster or alt to move slower.");
 			if (ui.is_hovered) {
 				zui_tooltip(orbit_and_rotate_tooltip + "\n\n" + fly_tooltip);
@@ -501,9 +509,11 @@ function ui_menu_render() {
 						if (update_version > 0) {
 							let date: string = config_get_date(); // 2019 -> 19
 							date = substring(date, 2, date.length);
-							let date_int: i32 = parseInt(string_replace_all(date, "-", ""));
+							let date_int: i32 = parse_int(string_replace_all(date, "-", ""));
 							if (update_version > date_int) {
-								ui_box_show_message(tr("Update"), tr("Update is available!\nPlease visit {url}.", new map_t([["url", manifest_url]])));
+								let vars: map_t<string, string> = map_create();
+								map_set(vars, "url", manifest_url);
+								ui_box_show_message(tr("Update"), tr("Update is available!\nPlease visit {url}.", vars));
 							}
 							else {
 								ui_box_show_message(tr("Update"), tr("You are up to date!"));
@@ -511,7 +521,9 @@ function ui_menu_render() {
 						}
 					}
 					else {
-						ui_box_show_message(tr("Update"), tr("Unable to check for updates.\nPlease visit {url}.", new map_t([["url", manifest_url]])));
+						let vars: map_t<string, string> = map_create();
+						map_set(vars, "url", manifest_url);
+						ui_box_show_message(tr("Update"), tr("Unable to check for updates.\nPlease visit {url}.", vars));
 					}
 				});
 				///end
@@ -526,7 +538,7 @@ function ui_menu_render() {
 				let save: string = (path_is_protected() ? krom_save_path() : path_data()) + path_sep + "tmp.txt";
 				krom_sys_command('wmic path win32_VideoController get name > "' + save + '"');
 				let blob: buffer_t = krom_load_blob(save);
-				let u8: u8_array_t = new u8_array_t(blob);
+				let u8: u8_array_t = u8_array_create_from_buffer(blob);
 				let gpu_raw: string = "";
 				for (let i: i32 = 0; i < math_floor(u8.length / 2); ++i) {
 					let c: string = string_from_char_code(u8[i * 2]);
@@ -536,7 +548,8 @@ function ui_menu_render() {
 				let gpus: string[] = string_split(gpu_raw, "\n");
 				array_splice(gpus, 1, gpus.length - 2);
 				let gpu: string = "";
-				for (let g of gpus) {
+				for (let i: i32 = 0; i < gpus.length; ++i) {
+					let g: string = gpus[i];
 					gpu += trim_end(g) + ", ";
 				}
 				gpu = substring(gpu, 0, gpu.length - 2);

+ 62 - 31
base/Sources/ui_nodes.ts

@@ -69,7 +69,8 @@ function ui_nodes_on_link_drag(link_drag_id: i32, is_new_link: bool) {
 					// Connect to the first socket
 					link_drag.to_socket = 0;
 					// Try to find the first type-matching socket and use it if present
-					for (let socket of n.inputs) {
+					for (let i: i32 = 0; i < n.inputs.length; ++i) {
+						let socket: zui_node_socket_t = n.inputs[i];
 						if (socket.type == from_type) {
 							link_drag.to_socket = array_index_of(n.inputs, socket);
 							break;
@@ -217,7 +218,8 @@ function ui_nodes_on_canvas_released() {
 		let nodes: zui_nodes_t = ui_nodes_get_nodes();
 		let canvas: zui_node_canvas_t = ui_nodes_get_canvas(true);
 		let selected: zui_node_t = null;
-		for (let node of canvas.nodes) {
+		for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+			let node: zui_node_t = canvas.nodes[i];
 			if (zui_get_input_in_rect(ui_nodes_ui._window_x + zui_nodes_NODE_X(node), ui_nodes_ui._window_y + zui_nodes_NODE_Y(node), zui_nodes_NODE_W(node), zui_nodes_NODE_H(canvas, node))) {
 				selected = node;
 				break;
@@ -313,7 +315,8 @@ function ui_nodes_on_canvas_released() {
 	if (ui_nodes_ui.input_released) {
 		let nodes: zui_nodes_t = ui_nodes_get_nodes();
 		let canvas: zui_node_canvas_t = ui_nodes_get_canvas(true);
-		for (let node of canvas.nodes) {
+		for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+			let node: zui_node_t = canvas.nodes[i];
 			if (zui_get_input_in_rect(ui_nodes_ui._window_x + zui_nodes_NODE_X(node), ui_nodes_ui._window_y + zui_nodes_NODE_Y(node), zui_nodes_NODE_W(node), zui_nodes_NODE_H(canvas, node))) {
 				if (node.id == nodes.nodes_selected_id[0]) {
 					ui_view2d_hwnd.redraws = 2;
@@ -484,22 +487,26 @@ function ui_nodes_update() {
 	let nodes: zui_nodes_t = ui_nodes_get_nodes();
 	if (nodes.nodes_selected_id.length > 0 && ui_nodes_ui.is_key_pressed) {
 		if (ui_nodes_ui.key == key_code_t.LEFT) {
-			for (let n of nodes.nodes_selected_id) {
+			for (let i: i32 = 0; i < nodes.nodes_selected_id.length; ++i) {
+				let n: i32 = nodes.nodes_selected_id[i];
 				zui_get_node(ui_nodes_get_canvas(true).nodes, n).x -= 1;
 			}
 		}
 		else if (ui_nodes_ui.key == key_code_t.RIGHT) {
-			for (let n of nodes.nodes_selected_id) {
+			for (let i: i32 = 0; i < nodes.nodes_selected_id.length; ++i) {
+				let n: i32 = nodes.nodes_selected_id[i];
 				zui_get_node(ui_nodes_get_canvas(true).nodes, n).x += 1;
 			}
 		}
 		if (ui_nodes_ui.key == key_code_t.UP) {
-			for (let n of nodes.nodes_selected_id) {
+			for (let i: i32 = 0; i < nodes.nodes_selected_id.length; ++i) {
+				let n: i32 = nodes.nodes_selected_id[i];
 				zui_get_node(ui_nodes_get_canvas(true).nodes, n).y -= 1;
 			}
 		}
 		else if (ui_nodes_ui.key == key_code_t.DOWN) {
-			for (let n of nodes.nodes_selected_id) {
+			for (let i: i32 = 0; i < nodes.nodes_selected_id.length; ++i) {
+				let n: i32 = nodes.nodes_selected_id[i];
 				zui_get_node(ui_nodes_get_canvas(true).nodes, n).y += 1;
 			}
 		}
@@ -564,8 +571,10 @@ function ui_nodes_node_search(x: i32 = -1, y: i32 = -1, done: ()=>void = null) {
 		let node_list: zui_node_t[][] = nodes_brush_list;
 		///end
 
-		for (let list of node_list) {
-			for (let n of list) {
+		for (let i: i32 = 0; i < node_list.length; ++i) {
+			let list: zui_node_t[] = node_list[i];
+			for (let i: i32 = 0; i < list.length; ++i) {
+				let n: zui_node_t = list[i];
 				if (string_index_of(to_lower_case(tr(n.name)), search) >= 0) {
 					ui.t.BUTTON_COL = count == ui_nodes_node_search_offset ? ui.t.HIGHLIGHT_COL : ui.t.SEPARATOR_COL;
 					if (zui_button(tr(n.name), zui_align_t.LEFT) || (enter && count == ui_nodes_node_search_offset)) {
@@ -851,8 +860,10 @@ function ui_nodes_render() {
 					continue;
 				}
 				let found: bool = false;
-				for (let list of node_list) {
-					for (let list_node of list) {
+				for (let i: i32 = 0; i < node_list.length; ++i) {
+					let list: zui_node_t[] = node_list[i];
+					for (let i: i32 = 0; i < list.length; ++i) {
+						let list_node: zui_node_t = list[i];
 						if (canvas_node.type == list_node.type) {
 							found = true;
 							break;
@@ -994,7 +1005,8 @@ function ui_nodes_render() {
 		if (h.changed) { // Check whether renaming is possible and update group links
 			if (ui_nodes_group_stack.length > 0) {
 				let can_rename: bool = true;
-				for (let m of project_material_groups) {
+				for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+					let m: node_group_t = project_material_groups[i];
 					if (m.canvas.name == new_name) {
 						can_rename = false; // Name already used
 					}
@@ -1004,14 +1016,18 @@ function ui_nodes_render() {
 					let old_name: string = c.name;
 					c.name = new_name;
 					let canvases: zui_node_canvas_t[] = [];
-					for (let m of project_materials) {
+					for (let i: i32 = 0; i < project_materials.length; ++i) {
+						let m: slot_material_t = project_materials[i];
 						array_push(canvases, m.canvas);
 					}
-					for (let m of project_material_groups) {
+					for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+						let m: node_group_t = project_material_groups[i];
 						array_push(canvases, m.canvas);
 					}
-					for (let canvas of canvases) {
-						for (let n of canvas.nodes) {
+					for (let i: i32 = 0; i < canvases.length; ++i) {
+						let canvas: zui_node_canvas_t = canvases[i];
+						for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
+							let n: zui_node_t = canvas.nodes[i];
 							if (n.type == "GROUP" && n.name == old_name) {
 								n.name = c.name;
 							}
@@ -1122,7 +1138,8 @@ function ui_nodes_render() {
 
 		ui_menu_start(ui_nodes_ui);
 
-		for (let n of list[ui_nodes_menu_category]) {
+		for (let i: i32 = 0; i < list[ui_nodes_menu_category].length; ++i) {
+			let n: zui_node_t = list[ui_nodes_menu_category][i];
 			if (ui_menu_button(ui_nodes_ui, tr(n.name))) {
 				ui_nodes_push_undo();
 				let canvas: zui_node_canvas_t = ui_nodes_get_canvas(true);
@@ -1142,7 +1159,8 @@ function ui_nodes_render() {
 			}
 		}
 		if (is_group_category) {
-			for (let g of project_material_groups) {
+			for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+				let g: node_group_t = project_material_groups[i];
 				zui_fill(0, 1, ui_nodes_ui._w / zui_SCALE(ui_nodes_ui), ui_nodes_ui.t.BUTTON_H + 2, ui_nodes_ui.t.ACCENT_SELECT_COL);
 				zui_fill(1, 1, ui_nodes_ui._w / zui_SCALE(ui_nodes_ui) - 2, ui_nodes_ui.t.BUTTON_H + 1, ui_nodes_ui.t.SEPARATOR_COL);
 				ui_nodes_ui.enabled = ui_nodes_can_place_group(g.canvas.name);
@@ -1189,7 +1207,8 @@ function ui_nodes_contains_node_group_recursive(group: node_group_t, group_name:
 	if (group.canvas.name == group_name) {
 		return true;
 	}
-	for (let n of group.canvas.nodes) {
+	for (let i: i32 = 0; i < group.canvas.nodes.length; ++i) {
+		let n: zui_node_t = group.canvas.nodes[i];
 		if (n.type == "GROUP") {
 			let g: node_group_t = project_get_material_group_by_name(n.name);
 			if (g != null && ui_nodes_contains_node_group_recursive(g, group_name)) {
@@ -1204,13 +1223,15 @@ function ui_nodes_can_place_group(group_name: string): bool {
 	// Prevent Recursive node groups
 	// The group to place must not contain the current group or a group that contains the current group
 	if (ui_nodes_group_stack.length > 0) {
-		for (let g of ui_nodes_group_stack) {
+		for (let i: i32 = 0; i < ui_nodes_group_stack.length; ++i) {
+			let g: node_group_t = ui_nodes_group_stack[i];
 			if (ui_nodes_contains_node_group_recursive(project_get_material_group_by_name(group_name), g.canvas.name)) return false;
 		}
 	}
 	// Group was deleted / renamed
 	let group_exists: bool = false;
-	for (let group of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let group: node_group_t = project_material_groups[i];
 		if (group_name == group.canvas.name) {
 			group_exists = true;
 		}
@@ -1296,12 +1317,14 @@ function ui_nodes_make_node(n: zui_node_t, nodes: zui_nodes_t, canvas: zui_node_
 	node.x = ui_nodes_get_node_x();
 	node.y = ui_nodes_get_node_y();
 	let count: i32 = 0;
-	for (let soc of node.inputs) {
+	for (let i: i32 = 0; i < node.inputs.length; ++i) {
+		let soc: zui_node_socket_t = node.inputs[i];
 		soc.id = zui_get_socket_id(canvas.nodes) + count;
 		soc.node_id = node.id;
 		count++;
 	}
-	for (let soc of node.outputs) {
+	for (let i: i32 = 0; i < node.outputs.length; ++i) {
+		let soc: zui_node_socket_t = node.outputs[i];
 		soc.id = zui_get_socket_id(canvas.nodes) + count;
 		soc.node_id = node.id;
 		count++;
@@ -1318,9 +1341,11 @@ function ui_nodes_make_group_node(group_canvas: zui_node_canvas_t, nodes: zui_no
 	node.y = ui_nodes_get_node_y();
 	let group_input: zui_node_t = null;
 	let group_output: zui_node_t = null;
-	for (let g of project_material_groups) {
+	for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+		let g: node_group_t = project_material_groups[i];
 		if (g.canvas.name == node.name) {
-			for (let n of g.canvas.nodes) {
+			for (let i: i32 = 0; i < g.canvas.nodes.length; ++i) {
+				let n: zui_node_t = g.canvas.nodes[i];
 				if (n.type == "GROUP_INPUT") {
 					group_input = n;
 				}
@@ -1332,10 +1357,12 @@ function ui_nodes_make_group_node(group_canvas: zui_node_canvas_t, nodes: zui_no
 		}
 	}
 	if (group_input != null && group_output != null) {
-		for (let soc of group_input.outputs) {
+		for (let i: i32 = 0; i < group_input.outputs.length; ++i) {
+			let soc: zui_node_socket_t = group_input.outputs[i];
 			array_push(node.inputs, nodes_material_create_socket(nodes, node, soc.name, soc.type, canvas, soc.min, soc.max, soc.default_value));
 		}
-		for (let soc of group_output.inputs) {
+		for (let i: i32 = 0; i < group_output.inputs.length; ++i) {
+			let soc: zui_node_socket_t = group_output.inputs[i];
 			array_push(node.outputs, nodes_material_create_socket(nodes, node, soc.name, soc.type, canvas, soc.min, soc.max, soc.default_value));
 		}
 	}
@@ -1375,7 +1402,8 @@ function ui_nodes_make_node_preview() {
 ///end
 
 function ui_nodes_has_group(c: zui_node_canvas_t): bool {
-	for (let n of c.nodes) {
+	for (let i: i32 = 0; i < c.nodes.length; ++i) {
+		let n: zui_node_t = c.nodes[i];
 		if (n.type == "GROUP") {
 			return true;
 		}
@@ -1384,11 +1412,13 @@ function ui_nodes_has_group(c: zui_node_canvas_t): bool {
 }
 
 function ui_nodes_traverse_group(mgroups: zui_node_canvas_t[], c: zui_node_canvas_t) {
-	for (let n of c.nodes) {
+	for (let i: i32 = 0; i < c.nodes.length; ++i) {
+		let n: zui_node_t = c.nodes[i];
 		if (n.type == "GROUP") {
 			if (ui_nodes_get_group(mgroups, n.name) == null) {
 				let canvases: zui_node_canvas_t[] = [];
-				for (let g of project_material_groups) {
+				for (let i: i32 = 0; i < project_material_groups.length; ++i) {
+					let g: node_group_t = project_material_groups[i];
 					array_push(canvases, g.canvas);
 				}
 				let group: zui_node_canvas_t = ui_nodes_get_group(canvases, n.name);
@@ -1400,7 +1430,8 @@ function ui_nodes_traverse_group(mgroups: zui_node_canvas_t[], c: zui_node_canva
 }
 
 function ui_nodes_get_group(canvases: zui_node_canvas_t[], name: string): zui_node_canvas_t {
-	for (let c of canvases) {
+	for (let i: i32 = 0; i < canvases.length; ++i) {
+		let c: zui_node_canvas_t = canvases[i];
 		if (c.name == name) {
 			return c;
 		}

+ 2 - 1
base/Sources/ui_status.ts

@@ -27,7 +27,8 @@ function ui_status_render_ui() {
 		g2_fill_rect(ui._window_w - 1, 0, 1, ui._window_h);
 
 		// Draw tabs
-		for (let draw of ui_base_hwnd_tabs[tab_area_t.STATUS]) {
+		for (let i: i32 = 0; i < ui_base_hwnd_tabs[tab_area_t.STATUS].length; ++i) {
+			let draw: any = ui_base_hwnd_tabs[tab_area_t.STATUS][i];
 			draw(ui_base_htabs[tab_area_t.STATUS]);
 		}
 

+ 15 - 5
base/Sources/ui_toolbar.ts

@@ -87,13 +87,23 @@ function ui_toolbar_render_ui() {
 		}
 		ui._y -= 4 * zui_SCALE(ui);
 
+		let vars_brush: map_t<string, string> = map_create();
+		map_set(vars_brush, "key", config_keymap.brush_ruler);
+		map_set(vars_brush, "action_paint", config_keymap.action_paint);
+
+		let vars_decal: map_t<string, string> = map_create();
+		map_set(vars_decal, "key", config_keymap.decal_mask);
+
+		let vars_clone: map_t<string, string> = map_create();
+		map_set(vars_clone, "key", config_keymap.set_clone_source);
+
 		let keys: string[] = [
-			"(" + config_keymap.tool_brush + ") - " + tr("Hold {action_paint} to paint\nHold {key} and press {action_paint} to paint a straight line (ruler mode)", new map_t([["key", config_keymap.brush_ruler], ["action_paint", config_keymap.action_paint]])),
-			"(" + config_keymap.tool_eraser + ") - " + tr("Hold {action_paint} to erase\nHold {key} and press {action_paint} to erase a straight line (ruler mode)", new map_t([["key", config_keymap.brush_ruler], ["action_paint", config_keymap.action_paint]])),
+			"(" + config_keymap.tool_brush + ") - " + tr("Hold {action_paint} to paint\nHold {key} and press {action_paint} to paint a straight line (ruler mode)", vars_brush),
+			"(" + config_keymap.tool_eraser + ") - " + tr("Hold {action_paint} to erase\nHold {key} and press {action_paint} to erase a straight line (ruler mode)", vars_brush),
 			"(" + config_keymap.tool_fill + ")",
-			"(" + config_keymap.tool_decal + ") - " + tr("Hold {key} to paint on a decal mask", new map_t([["key", config_keymap.decal_mask]])),
-			"(" + config_keymap.tool_text + ") - " + tr("Hold {key} to use the text as a mask", new map_t([["key", config_keymap.decal_mask]])),
-			"(" + config_keymap.tool_clone + ") - " + tr("Hold {key} to set source", new map_t([["key", config_keymap.set_clone_source]])),
+			"(" + config_keymap.tool_decal + ") - " + tr("Hold {key} to paint on a decal mask", vars_decal),
+			"(" + config_keymap.tool_text + ") - " + tr("Hold {key} to use the text as a mask", vars_decal),
+			"(" + config_keymap.tool_clone + ") - " + tr("Hold {key} to set source", vars_clone),
 			"(" + config_keymap.tool_blur + ")",
 			"(" + config_keymap.tool_smudge + ")",
 			"(" + config_keymap.tool_particle + ")",

+ 325 - 337
base/Sources/uniforms_ext.ts

@@ -20,132 +20,128 @@ function uniforms_ext_i32_link(object: object_t, mat: material_data_t, link: str
 }
 
 function uniforms_ext_f32_link(object: object_t, mat: material_data_t, link: string): Null<f32> {
-	switch (link) {
-		case "_brushRadius": {
-			///if (is_paint || is_sculpt)
-			let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-			let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
-			let brush_decal_mask_radius: f32 = context_raw.brush_decal_mask_radius;
-			if (config_raw.brush_3d) {
-				brush_decal_mask_radius *= context_raw.paint2d ? 0.55 * ui_view2d_pan_scale : 2.0;
-			}
-			let radius: f32 = decal_mask ? brush_decal_mask_radius : context_raw.brush_radius;
-			let val: f32 = (radius * context_raw.brush_nodes_radius) / 15.0;
-			if (config_raw.pressure_radius && pen_down()) {
-				val *= pen_pressure * config_raw.pressure_sensitivity;
-			}
-			let scale2d: f32 = (900 / base_h()) * config_raw.window_scale;
-
-			if (config_raw.brush_3d && !decal) {
-				val *= context_raw.paint2d ? 0.55 * scale2d * ui_view2d_pan_scale : 2;
-			}
-			else {
-				val *= scale2d; // Projection ratio
-			}
-			///end
-
-			///if is_lab
-			let radius: f32 = context_raw.brush_radius;
-			let val: f32 = radius / 15.0;
-			if (config_raw.pressure_radius && pen_down()) {
-				val *= pen_pressure * config_raw.pressure_sensitivity;
-			}
-			val *= 2;
-			///end
-
-			return val;
-		}
-		case "_vignetteStrength": {
-			return config_raw.rp_vignette;
-		}
-		case "_grainStrength": {
-			return config_raw.rp_grain;
-		}
-		case "_coneOffset": {
-			return context_raw.vxao_offset;
-		}
-		case "_coneAperture": {
-			return context_raw.vxao_aperture;
-		}
-		case "_bloomSampleScale": {
-			return render_path_base_bloom_sample_scale;
-		}
-
+	if (link == "_brushRadius") {
 		///if (is_paint || is_sculpt)
-		case "_brushScaleX": {
-			return 1 / context_raw.brush_scale_x;
-		}
-		case "_brushOpacity": {
-			let val: f32 = context_raw.brush_opacity * context_raw.brush_nodes_opacity;
-			if (config_raw.pressure_opacity && pen_down()) {
-				val *= pen_pressure * config_raw.pressure_sensitivity;
-			}
-			return val;
-		}
-		case "_brushHardness": {
-			let decal_mask: bool = operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
-			if (context_raw.tool != workspace_tool_t.BRUSH && context_raw.tool != workspace_tool_t.ERASER && context_raw.tool != workspace_tool_t.CLONE && !decal_mask) {
-				return 1.0;
-			}
-			let val: f32 = context_raw.brush_hardness * context_raw.brush_nodes_hardness;
-			if (config_raw.pressure_hardness && pen_down()) {
-				val *= pen_pressure * config_raw.pressure_sensitivity;
-			}
-			if (config_raw.brush_3d) {
-				if (context_raw.paint2d) {
-					val *= 1.0 / ui_view2d_pan_scale;
-				}
-				else {
-					val *= val;
-				}
-			}
-			return val;
+		let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
+		let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+		let brush_decal_mask_radius: f32 = context_raw.brush_decal_mask_radius;
+		if (config_raw.brush_3d) {
+			brush_decal_mask_radius *= context_raw.paint2d ? 0.55 * ui_view2d_pan_scale : 2.0;
 		}
-		case "_brushScale": {
-			let fill: bool = context_raw.layer.fill_layer != null;
-			let val: f32 = (fill ? context_raw.layer.scale : context_raw.brush_scale) * context_raw.brush_nodes_scale;
-			return val;
+		let radius: f32 = decal_mask ? brush_decal_mask_radius : context_raw.brush_radius;
+		let val: f32 = (radius * context_raw.brush_nodes_radius) / 15.0;
+		if (config_raw.pressure_radius && pen_down()) {
+			val *= pen_pressure * config_raw.pressure_sensitivity;
 		}
-		case "_objectId": {
-			return array_index_of(project_paint_objects, object.ext);
+		let scale2d: f32 = (900 / base_h()) * config_raw.window_scale;
+
+		if (config_raw.brush_3d && !decal) {
+			val *= context_raw.paint2d ? 0.55 * scale2d * ui_view2d_pan_scale : 2;
 		}
-		///if is_paint
-		case "_dilateRadius": {
-			return util_uv_dilatemap != null ? config_raw.dilate_radius : 0.0;
+		else {
+			val *= scale2d; // Projection ratio
 		}
 		///end
-		case "_decalLayerDim": {
-			return mat4_get_scale(context_raw.layer.decal_mat).z * 0.5;
-		}
-		case "_pickerOpacity": {
-			return context_raw.picked_color.opacity;
+
+		///if is_lab
+		let radius: f32 = context_raw.brush_radius;
+		let val: f32 = radius / 15.0;
+		if (config_raw.pressure_radius && pen_down()) {
+			val *= pen_pressure * config_raw.pressure_sensitivity;
 		}
-		case "_pickerOcclusion": {
-			return context_raw.picked_color.occlusion;
+		val *= 2;
+		///end
+
+		return val;
+	}
+	else if (link == "_vignetteStrength") {
+		return config_raw.rp_vignette;
+	}
+	else if (link == "_grainStrength") {
+		return config_raw.rp_grain;
+	}
+	else if (link == "_coneOffset") {
+		return context_raw.vxao_offset;
+	}
+	else if (link == "_coneAperture") {
+		return context_raw.vxao_aperture;
+	}
+	else if (link == "_bloomSampleScale") {
+		return render_path_base_bloom_sample_scale;
+	}
+
+	///if (is_paint || is_sculpt)
+	else if (link == "_brushScaleX") {
+		return 1 / context_raw.brush_scale_x;
+	}
+	else if (link == "_brushOpacity") {
+		let val: f32 = context_raw.brush_opacity * context_raw.brush_nodes_opacity;
+		if (config_raw.pressure_opacity && pen_down()) {
+			val *= pen_pressure * config_raw.pressure_sensitivity;
 		}
-		case "_pickerRoughness": {
-			return context_raw.picked_color.roughness;
+		return val;
+	}
+	else if (link == "_brushHardness") {
+		let decal_mask: bool = operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+		if (context_raw.tool != workspace_tool_t.BRUSH && context_raw.tool != workspace_tool_t.ERASER && context_raw.tool != workspace_tool_t.CLONE && !decal_mask) {
+			return 1.0;
 		}
-		case "_pickerMetallic": {
-			return context_raw.picked_color.metallic;
+		let val: f32 = context_raw.brush_hardness * context_raw.brush_nodes_hardness;
+		if (config_raw.pressure_hardness && pen_down()) {
+			val *= pen_pressure * config_raw.pressure_sensitivity;
 		}
-		case "_pickerHeight": {
-			return context_raw.picked_color.height;
+		if (config_raw.brush_3d) {
+			if (context_raw.paint2d) {
+				val *= 1.0 / ui_view2d_pan_scale;
+			}
+			else {
+				val *= val;
+			}
 		}
-		///end
+		return val;
+	}
+	else if (link == "_brushScale") {
+		let fill: bool = context_raw.layer.fill_layer != null;
+		let val: f32 = (fill ? context_raw.layer.scale : context_raw.brush_scale) * context_raw.brush_nodes_scale;
+		return val;
+	}
+	else if (link == "_objectId") {
+		return array_index_of(project_paint_objects, object.ext);
+	}
+	///if is_paint
+	else if (link == "_dilateRadius") {
+		return util_uv_dilatemap != null ? config_raw.dilate_radius : 0.0;
+	}
+	///end
+	else if (link == "_decalLayerDim") {
+		return mat4_get_scale(context_raw.layer.decal_mat).z * 0.5;
+	}
+	else if (link == "_pickerOpacity") {
+		return context_raw.picked_color.opacity;
+	}
+	else if (link == "_pickerOcclusion") {
+		return context_raw.picked_color.occlusion;
+	}
+	else if (link == "_pickerRoughness") {
+		return context_raw.picked_color.roughness;
 	}
+	else if (link == "_pickerMetallic") {
+		return context_raw.picked_color.metallic;
+	}
+	else if (link == "_pickerHeight") {
+		return context_raw.picked_color.height;
+	}
+	///end
+
 	if (parser_material_script_links != null) {
-		for (let key of parser_material_script_links.keys()) {
+		let keys: string[] = map_keys_to_array(parser_material_script_links);
+		for (let i: i32 = 0; i < keys.length; ++i) {
+			let key: string = keys[i];
 			let asciprt_links: any = parser_material_script_links;
 			let script: string = asciprt_links[key];
 			let result: f32 = 0.0;
 			if (script != "") {
-				try {
-					result = eval(script);
-				}
-				catch(e: any) {
-					console_log(e);
-				}
+				result = js_eval(script);
 			}
 			return result;
 		}
@@ -154,104 +150,102 @@ function uniforms_ext_f32_link(object: object_t, mat: material_data_t, link: str
 }
 
 function uniforms_ext_vec2_link(object: object_t, mat: material_data_t, link: string): vec4_t {
-	switch (link) {
-		case "_gbufferSize": {
-			vec4_set(uniforms_ext_vec, 0, 0, 0);
-			let gbuffer2: render_target_t = map_get(render_path_render_targets, "gbuffer2");
-			vec4_set(uniforms_ext_vec, gbuffer2._image.width, gbuffer2._image.height, 0);
-			return uniforms_ext_vec;
-		}
-		case "_cloneDelta": {
-			vec4_set(uniforms_ext_vec, context_raw.clone_delta_x, context_raw.clone_delta_y, 0);
-			return uniforms_ext_vec;
-		}
-		case "_texpaintSize": {
-			vec4_set(uniforms_ext_vec, config_get_texture_res_x(), config_get_texture_res_y(), 0);
-			return uniforms_ext_vec;
-		}
-		///if (is_paint || is_sculpt)
-		case "_brushAngle": {
-			let brush_angle: f32 = context_raw.brush_angle + context_raw.brush_nodes_angle;
-			let angle: f32 = context_raw.layer.fill_layer != null ? context_raw.layer.angle : brush_angle;
-			angle *= (math_pi() / 180);
-			if (config_raw.pressure_angle && pen_down()) {
-				angle *= pen_pressure * config_raw.pressure_sensitivity;
-			}
-			vec4_set(uniforms_ext_vec, math_cos(-angle), math_sin(-angle), 0);
-			return uniforms_ext_vec;
-		}
-		///end
+	if (link == "_gbufferSize") {
+		vec4_set(uniforms_ext_vec, 0, 0, 0);
+		let gbuffer2: render_target_t = map_get(render_path_render_targets, "gbuffer2");
+		vec4_set(uniforms_ext_vec, gbuffer2._image.width, gbuffer2._image.height, 0);
+		return uniforms_ext_vec;
+	}
+	else if (link == "_cloneDelta") {
+		vec4_set(uniforms_ext_vec, context_raw.clone_delta_x, context_raw.clone_delta_y, 0);
+		return uniforms_ext_vec;
+	}
+	else if (link == "_texpaintSize") {
+		vec4_set(uniforms_ext_vec, config_get_texture_res_x(), config_get_texture_res_y(), 0);
+		return uniforms_ext_vec;
 	}
+	///if (is_paint || is_sculpt)
+	else if (link == "_brushAngle") {
+		let brush_angle: f32 = context_raw.brush_angle + context_raw.brush_nodes_angle;
+		let angle: f32 = context_raw.layer.fill_layer != null ? context_raw.layer.angle : brush_angle;
+		angle *= (math_pi() / 180);
+		if (config_raw.pressure_angle && pen_down()) {
+			angle *= pen_pressure * config_raw.pressure_sensitivity;
+		}
+		vec4_set(uniforms_ext_vec, math_cos(-angle), math_sin(-angle), 0);
+		return uniforms_ext_vec;
+	}
+	///end
+
 	return null;
 }
 
 function uniforms_ext_vec3_link(object: object_t, mat: material_data_t, link: string): vec4_t {
 	let v: vec4_t = null;
-	switch (link) {
-		///if (is_paint || is_sculpt)
-		case "_brushDirection": {
-			v = _uniforms_vec;
-			// Discard first paint for directional brush
-			let allow_paint: bool = context_raw.prev_paint_vec_x != context_raw.last_paint_vec_x &&
-									context_raw.prev_paint_vec_y != context_raw.last_paint_vec_y &&
-									context_raw.prev_paint_vec_x > 0 &&
-									context_raw.prev_paint_vec_y > 0;
-			let x: f32 = context_raw.paint_vec.x;
-			let y: f32 = context_raw.paint_vec.y;
-			let lastx: f32 = context_raw.prev_paint_vec_x;
-			let lasty: f32 = context_raw.prev_paint_vec_y;
-			if (context_raw.paint2d) {
-				x = vec2d(x);
-				lastx = vec2d(lastx);
-			}
-			let angle: f32 = math_atan2(-y + lasty, x - lastx) - math_pi() / 2;
-			vec4_set(v, math_cos(angle), math_sin(angle), allow_paint ? 1 : 0);
-			context_raw.prev_paint_vec_x = context_raw.last_paint_vec_x;
-			context_raw.prev_paint_vec_y = context_raw.last_paint_vec_y;
-			return v;
-		}
-		case "_decalLayerLoc": {
-			v = _uniforms_vec;
-			vec4_set(v, context_raw.layer.decal_mat.m[12], context_raw.layer.decal_mat.m[13], context_raw.layer.decal_mat.m[14]);
-			return v;
-		}
-		case "_decalLayerNor": {
-			v = _uniforms_vec;
-			vec4_normalize(vec4_set(v, context_raw.layer.decal_mat.m[8], context_raw.layer.decal_mat.m[9], context_raw.layer.decal_mat.m[10]));
-			return v;
-		}
-		case "_pickerBase": {
-			v = _uniforms_vec;
-			vec4_set(v,
-				color_get_rb(context_raw.picked_color.base) / 255,
-				color_get_gb(context_raw.picked_color.base) / 255,
-				color_get_bb(context_raw.picked_color.base) / 255
-			);
-			return v;
-		}
-		case "_pickerNormal": {
-			v = _uniforms_vec;
-			vec4_set(v,
-				color_get_rb(context_raw.picked_color.normal) / 255,
-				color_get_gb(context_raw.picked_color.normal) / 255,
-				color_get_bb(context_raw.picked_color.normal) / 255
-			);
-			return v;
-		}
-		///if arm_physics
-		case "_particleHit": {
-			v = _uniforms_vec;
-			vec4_set(v, context_raw.particle_hit_x, context_raw.particle_hit_y, context_raw.particle_hit_z);
-			return v;
-		}
-		case "_particleHitLast": {
-			v = _uniforms_vec;
-			vec4_set(v, context_raw.last_particle_hit_x, context_raw.last_particle_hit_y, context_raw.last_particle_hit_z);
-			return v;
-		}
-		///end
-		///end
+
+	///if (is_paint || is_sculpt)
+	if (link == "_brushDirection") {
+		v = _uniforms_vec;
+		// Discard first paint for directional brush
+		let allow_paint: bool = context_raw.prev_paint_vec_x != context_raw.last_paint_vec_x &&
+								context_raw.prev_paint_vec_y != context_raw.last_paint_vec_y &&
+								context_raw.prev_paint_vec_x > 0 &&
+								context_raw.prev_paint_vec_y > 0;
+		let x: f32 = context_raw.paint_vec.x;
+		let y: f32 = context_raw.paint_vec.y;
+		let lastx: f32 = context_raw.prev_paint_vec_x;
+		let lasty: f32 = context_raw.prev_paint_vec_y;
+		if (context_raw.paint2d) {
+			x = vec2d(x);
+			lastx = vec2d(lastx);
+		}
+		let angle: f32 = math_atan2(-y + lasty, x - lastx) - math_pi() / 2;
+		vec4_set(v, math_cos(angle), math_sin(angle), allow_paint ? 1 : 0);
+		context_raw.prev_paint_vec_x = context_raw.last_paint_vec_x;
+		context_raw.prev_paint_vec_y = context_raw.last_paint_vec_y;
+		return v;
 	}
+	else if (link == "_decalLayerLoc") {
+		v = _uniforms_vec;
+		vec4_set(v, context_raw.layer.decal_mat.m[12], context_raw.layer.decal_mat.m[13], context_raw.layer.decal_mat.m[14]);
+		return v;
+	}
+	else if (link == "_decalLayerNor") {
+		v = _uniforms_vec;
+		vec4_normalize(vec4_set(v, context_raw.layer.decal_mat.m[8], context_raw.layer.decal_mat.m[9], context_raw.layer.decal_mat.m[10]));
+		return v;
+	}
+	else if (link == "_pickerBase") {
+		v = _uniforms_vec;
+		vec4_set(v,
+			color_get_rb(context_raw.picked_color.base) / 255,
+			color_get_gb(context_raw.picked_color.base) / 255,
+			color_get_bb(context_raw.picked_color.base) / 255
+		);
+		return v;
+	}
+	else if (link == "_pickerNormal") {
+		v = _uniforms_vec;
+		vec4_set(v,
+			color_get_rb(context_raw.picked_color.normal) / 255,
+			color_get_gb(context_raw.picked_color.normal) / 255,
+			color_get_bb(context_raw.picked_color.normal) / 255
+		);
+		return v;
+	}
+	///if arm_physics
+	else if (link == "_particleHit") {
+		v = _uniforms_vec;
+		vec4_set(v, context_raw.particle_hit_x, context_raw.particle_hit_y, context_raw.particle_hit_z);
+		return v;
+	}
+	else if (link == "_particleHitLast") {
+		v = _uniforms_vec;
+		vec4_set(v, context_raw.last_particle_hit_x, context_raw.last_particle_hit_y, context_raw.last_particle_hit_z);
+		return v;
+	}
+	///end
+	///end
 
 	return v;
 }
@@ -267,176 +261,170 @@ function vec2d(x: f32) {
 ///end
 
 function uniforms_ext_vec4_link(object: object_t, mat: material_data_t, link: string): vec4_t {
-	switch (link) {
-		case "_inputBrush": {
-			let down: bool = mouse_down() || pen_down();
-			vec4_set(uniforms_ext_vec, context_raw.paint_vec.x, context_raw.paint_vec.y, down ? 1.0 : 0.0, 0.0);
-
-			///if (is_paint || is_sculpt)
-			if (context_raw.paint2d) {
-				uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
-			}
-			///end
+	if (link == "_inputBrush") {
+		let down: bool = mouse_down() || pen_down();
+		vec4_set(uniforms_ext_vec, context_raw.paint_vec.x, context_raw.paint_vec.y, down ? 1.0 : 0.0, 0.0);
 
-			return uniforms_ext_vec;
+		///if (is_paint || is_sculpt)
+		if (context_raw.paint2d) {
+			uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
 		}
-		case "_inputBrushLast": {
-			let down: bool = mouse_down() || pen_down();
-			vec4_set(uniforms_ext_vec, context_raw.last_paint_vec_x, context_raw.last_paint_vec_y, down ? 1.0 : 0.0, 0.0);
+		///end
 
-			///if (is_paint || is_sculpt)
-			if (context_raw.paint2d) {
-				uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
-			}
-			///end
+		return uniforms_ext_vec;
+	}
+	else if (link == "_inputBrushLast") {
+		let down: bool = mouse_down() || pen_down();
+		vec4_set(uniforms_ext_vec, context_raw.last_paint_vec_x, context_raw.last_paint_vec_y, down ? 1.0 : 0.0, 0.0);
 
-			return uniforms_ext_vec;
-		}
-		case "_envmapData": {
-			vec4_set(uniforms_ext_vec, context_raw.envmap_angle, math_sin(-context_raw.envmap_angle), math_cos(-context_raw.envmap_angle), scene_world.strength);
-			return uniforms_ext_vec;
-		}
-		case "_envmapDataWorld": {
-			vec4_set(uniforms_ext_vec, context_raw.envmap_angle, math_sin(-context_raw.envmap_angle), math_cos(-context_raw.envmap_angle), context_raw.show_envmap ? scene_world.strength : 1.0);
-			return uniforms_ext_vec;
-		}
 		///if (is_paint || is_sculpt)
-		case "_stencilTransform": {
-			vec4_set(uniforms_ext_vec, context_raw.brush_stencil_x, context_raw.brush_stencil_y, context_raw.brush_stencil_scale, context_raw.brush_stencil_angle);
-			if (context_raw.paint2d) uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
-			return uniforms_ext_vec;
-		}
-		case "_decalMask": {
-			let decal_mask: bool = operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
-			let val: f32 = (context_raw.brush_radius * context_raw.brush_nodes_radius) / 15.0;
-			let scale2d: f32 = (900 / base_h()) * config_raw.window_scale;
-			val *= scale2d; // Projection ratio
-			vec4_set(uniforms_ext_vec, context_raw.decal_x, context_raw.decal_y, decal_mask ? 1 : 0, val);
-			if (context_raw.paint2d) {
-				uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
-			}
-			return uniforms_ext_vec;
+		if (context_raw.paint2d) {
+			uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
 		}
 		///end
+
+		return uniforms_ext_vec;
 	}
+	else if (link == "_envmapData") {
+		vec4_set(uniforms_ext_vec, context_raw.envmap_angle, math_sin(-context_raw.envmap_angle), math_cos(-context_raw.envmap_angle), scene_world.strength);
+		return uniforms_ext_vec;
+	}
+	else if (link == "_envmapDataWorld") {
+		vec4_set(uniforms_ext_vec, context_raw.envmap_angle, math_sin(-context_raw.envmap_angle), math_cos(-context_raw.envmap_angle), context_raw.show_envmap ? scene_world.strength : 1.0);
+		return uniforms_ext_vec;
+	}
+	///if (is_paint || is_sculpt)
+	else if (link == "_stencilTransform") {
+		vec4_set(uniforms_ext_vec, context_raw.brush_stencil_x, context_raw.brush_stencil_y, context_raw.brush_stencil_scale, context_raw.brush_stencil_angle);
+		if (context_raw.paint2d) uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
+		return uniforms_ext_vec;
+	}
+	else if (link == "_decalMask") {
+		let decal_mask: bool = operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+		let val: f32 = (context_raw.brush_radius * context_raw.brush_nodes_radius) / 15.0;
+		let scale2d: f32 = (900 / base_h()) * config_raw.window_scale;
+		val *= scale2d; // Projection ratio
+		vec4_set(uniforms_ext_vec, context_raw.decal_x, context_raw.decal_y, decal_mask ? 1 : 0, val);
+		if (context_raw.paint2d) {
+			uniforms_ext_vec.x = vec2d(uniforms_ext_vec.x);
+		}
+		return uniforms_ext_vec;
+	}
+	///end
+
 	return null;
 }
 
 function uniforms_ext_mat4_link(object: object_t, mat: material_data_t, link: string): mat4_t {
-	switch (link) {
-		///if (is_paint || is_sculpt)
-		case "_decalLayerMatrix": { // Decal layer
-			let m: mat4_t = _uniforms_mat;
-			mat4_set_from(m, context_raw.layer.decal_mat);
-			mat4_get_inv(m, m);
-			mat4_mult_mat(m, uniforms_ext_ortho_p);
-			return m;
-		}
-		///end
+	///if (is_paint || is_sculpt)
+	if (link == "_decalLayerMatrix") { // Decal layer
+		let m: mat4_t = _uniforms_mat;
+		mat4_set_from(m, context_raw.layer.decal_mat);
+		mat4_get_inv(m, m);
+		mat4_mult_mat(m, uniforms_ext_ortho_p);
+		return m;
 	}
+	///end
+
 	return null;
 }
 
 function uniforms_ext_tex_link(object: object_t, mat: material_data_t, link: string): image_t {
-	switch (link) {
-		case "_texpaint_undo": {
-			///if (is_paint || is_sculpt)
-			let i: i32 = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
-			return map_get(render_path_render_targets, "texpaint_undo" + i)._image;
-			///end
-
-			///if is_lab
-			return null;
-			///end
-		}
-		case "_texpaint_nor_undo": {
-			///if (is_paint || is_sculpt)
-			let i: i32 = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
-			return map_get(render_path_render_targets, "texpaint_nor_undo" + i)._image;
-			///end
-
-			///if is_lab
-			return null;
-			///end
-		}
-		case "_texpaint_pack_undo": {
-			///if (is_paint || is_sculpt)
-			let i: i32 = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
-			return map_get(render_path_render_targets, "texpaint_pack_undo" + i)._image;
-			///end
-
-			///if is_lab
-			return null;
-			///end
-		}
+	if (link == "_texpaint_undo") {
+		///if (is_paint || is_sculpt)
+		let i: i32 = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
+		return map_get(render_path_render_targets, "texpaint_undo" + i)._image;
+		///end
 
-		case "_ltcMat": {
-			if (const_data_ltc_mat_tex == null) {
-				const_data_init_ltc();
-			}
-			return const_data_ltc_mat_tex;
-		}
-		case "_ltcMag": {
-			if (const_data_ltc_mag_tex == null) {
-				const_data_init_ltc();
-			}
-			return const_data_ltc_mag_tex;
-		}
+		///if is_lab
+		return null;
+		///end
+	}
+	else if (link == "_texpaint_nor_undo") {
+		///if (is_paint || is_sculpt)
+		let i: i32 = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
+		return map_get(render_path_render_targets, "texpaint_nor_undo" + i)._image;
+		///end
 
+		///if is_lab
+		return null;
+		///end
+	}
+	else if (link == "_texpaint_pack_undo") {
 		///if (is_paint || is_sculpt)
-		case "_texcolorid": {
-			if (project_assets.length == 0) {
-				return map_get(render_path_render_targets, "empty_white")._image;
-			}
-			else {
-				return project_get_image(project_assets[context_raw.colorid_handle.position]);
-			}
-		}
-		case "_textexttool": { // Opacity map for text
-			return context_raw.text_tool_image;
+		let i: i32 = history_undo_i - 1 < 0 ? config_raw.undo_steps - 1 : history_undo_i - 1;
+		return map_get(render_path_render_targets, "texpaint_pack_undo" + i)._image;
+		///end
+
+		///if is_lab
+		return null;
+		///end
+	}
+	else if (link == "_ltcMat") {
+		if (const_data_ltc_mat_tex == null) {
+			const_data_init_ltc();
 		}
-		case "_texbrushmask": {
-			return context_raw.brush_mask_image;
+		return const_data_ltc_mat_tex;
+	}
+	else if (link == "_ltcMag") {
+		if (const_data_ltc_mag_tex == null) {
+			const_data_init_ltc();
 		}
-		case "_texbrushstencil": {
-			return context_raw.brush_stencil_image;
+		return const_data_ltc_mag_tex;
+	}
+	///if (is_paint || is_sculpt)
+	else if (link == "_texcolorid") {
+		if (project_assets.length == 0) {
+			return map_get(render_path_render_targets, "empty_white")._image;
 		}
-		case "_texparticle": {
-			return map_get(render_path_render_targets, "texparticle")._image;
+		else {
+			return project_get_image(project_assets[context_raw.colorid_handle.position]);
 		}
-		///end
+	}
+	else if (link == "_textexttool") { // Opacity map for text
+		return context_raw.text_tool_image;
+	}
+	else if (link == "_texbrushmask") {
+		return context_raw.brush_mask_image;
+	}
+	else if (link == "_texbrushstencil") {
+		return context_raw.brush_stencil_image;
+	}
+	else if (link == "_texparticle") {
+		return map_get(render_path_render_targets, "texparticle")._image;
+	}
+	///end
 
-		///if is_paint
-		case "_texuvmap": {
-			if (!util_uv_uvmap_cached) {
-				let _init = function () {
-					util_uv_cache_uv_map();
-				}
-				app_notify_on_init(_init);
-			}
-			return util_uv_uvmap;
-		}
-		case "_textrianglemap": {
-			if (!util_uv_trianglemap_cached) {
-				let _init = function () {
-					util_uv_cache_triangle_map();
-				}
-				app_notify_on_init(_init);
+	///if is_paint
+	else if (link == "_texuvmap") {
+		if (!util_uv_uvmap_cached) {
+			let _init = function () {
+				util_uv_cache_uv_map();
 			}
-			return util_uv_trianglemap;
+			app_notify_on_init(_init);
 		}
-		case "_texuvislandmap": {
+		return util_uv_uvmap;
+	}
+	else if (link == "_textrianglemap") {
+		if (!util_uv_trianglemap_cached) {
 			let _init = function () {
-				util_uv_cache_uv_island_map();
+				util_uv_cache_triangle_map();
 			}
 			app_notify_on_init(_init);
-			return util_uv_uvislandmap_cached ? util_uv_uvislandmap :map_get(render_path_render_targets, "empty_black")._image;
 		}
-		case "_texdilatemap": {
-			return util_uv_dilatemap;
+		return util_uv_trianglemap;
+	}
+	else if (link == "_texuvislandmap") {
+		let _init = function () {
+			util_uv_cache_uv_island_map();
 		}
-		///end
+		app_notify_on_init(_init);
+		return util_uv_uvislandmap_cached ? util_uv_uvislandmap :map_get(render_path_render_targets, "empty_black")._image;
 	}
+	else if (link == "_texdilatemap") {
+		return util_uv_dilatemap;
+	}
+	///end
 
 	if (starts_with(link, "_texpaint_pack_vert")) {
 		let tid: string = substring(link, link.length - 1, link.length);

+ 10 - 5
base/Sources/util_mesh.ts

@@ -113,7 +113,8 @@ function util_mesh_remove_merged() {
 
 function util_mesh_swap_axis(a: i32, b: i32) {
 	let objects: mesh_object_t[] = project_paint_objects;
-	for (let o of objects) {
+	for (let i: i32 = 0; i < objects.length; ++i) {
+		let o: mesh_object_t = objects[i];
 		// Remapping vertices, buckle up
 		// 0 - x, 1 - y, 2 - z
 		let vas: vertex_array_t[] = o.data.vertex_arrays;
@@ -154,7 +155,8 @@ function util_mesh_swap_axis(a: i32, b: i32) {
 
 function util_mesh_flip_normals() {
 	let objects: mesh_object_t[] = project_paint_objects;
-	for (let o of objects) {
+	for (let i: i32 = 0; i < objects.length; ++i) {
+		let o: mesh_object_t = objects[i];
 		let vas: vertex_array_t[] = o.data.vertex_arrays;
 		let va0: i16_array_t = vas[0].values;
 		let va1: i16_array_t = vas[1].values;
@@ -184,7 +186,8 @@ function util_mesh_calc_normals(smooth: bool = false) {
 	let cb: vec4_t = vec4_create();
 	let ab: vec4_t = vec4_create();
 	let objects: mesh_object_t[] = project_paint_objects;
-	for (let o of objects) {
+	for (let i: i32 = 0; i < objects.length; ++i) {
+		let o: mesh_object_t = objects[i];
 		let g: mesh_data_t = o.data;
 		let l: i32 = g4_vertex_struct_byte_size(g._.structure) / 2;
 		let inda: u32_array_t = g._.indices[0];
@@ -280,7 +283,8 @@ function util_mesh_to_origin() {
 	let dx: f32 = 0.0;
 	let dy: f32 = 0.0;
 	let dz: f32 = 0.0;
-	for (let o of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let o: mesh_object_t = project_paint_objects[i];
 		let l: i32 = 4;
 		let sc: f32 = o.data.scale_pos / 32767;
 		let va: i16_array_t = o.data.vertex_arrays[0].values;
@@ -318,7 +322,8 @@ function util_mesh_to_origin() {
 	dy /= project_paint_objects.length;
 	dz /= project_paint_objects.length;
 
-	for (let o of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let o: mesh_object_t = project_paint_objects[i];
 		let g: mesh_data_t = o.data;
 		let sc: f32 = o.data.scale_pos / 32767;
 		let va: i16_array_t = o.data.vertex_arrays[0].values;

+ 4 - 2
base/Sources/util_particle.ts

@@ -42,7 +42,8 @@ function util_particle_init() {
 		render_path_create_render_target(t);
 	}
 
-	for (let mat of _scene_raw.material_datas) {
+	for (let i: i32 = 0; i < _scene_raw.material_datas.length; ++i) {
+		let mat: material_data_t = _scene_raw.material_datas[i];
 		if (mat.name == "Material2") {
 			let m: material_data_t = json_parse(json_stringify(mat));
 			m.name = "MaterialParticle";
@@ -54,7 +55,8 @@ function util_particle_init() {
 	let md: material_data_t = data_get_material("Scene", "MaterialParticle");
 	context_raw.particle_material = md;
 
-	for (let obj of _scene_raw.objects) {
+	for (let i: i32 = 0; i < _scene_raw.objects.length; ++i) {
+		let obj: obj_t = _scene_raw.objects[i];
 		if (obj.name == ".Sphere") {
 			let particle: obj_t = json_parse(json_stringify(obj));
 			particle.name = ".Particle";

+ 2 - 1
base/Sources/util_render.ts

@@ -249,7 +249,8 @@ function util_render_make_brush_preview() {
 	// Set plane mesh
 	let painto: mesh_object_t = context_raw.paint_object;
 	let visibles: bool[] = [];
-	for (let p of project_paint_objects) {
+	for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
+		let p: mesh_object_t = project_paint_objects[i];
 		array_push(visibles, p.base.visible);
 		p.base.visible = false;
 	}

+ 4 - 2
base/Sources/viewport.ts

@@ -11,7 +11,8 @@ function viewport_scale_to_bounds() {
 	vec4_set(po.base.transform.scale, 2 / r, 2 / r, 2 / r);
 	vec4_set(po.base.transform.loc, 0, 0, 0);
 	transform_build_matrix(po.base.transform);
-	for (let c of po.base.children) {
+	for (let i: i32 = 0; i < po.base.children.length; ++i) {
+		let c: object_t = po.base.children[i];
 		vec4_set(c.transform.loc, 0, 0, 0);
 		transform_build_matrix(c.transform);
 	}
@@ -19,7 +20,8 @@ function viewport_scale_to_bounds() {
 
 function viewport_reset() {
 	let cam: camera_object_t = scene_camera;
-	for (let o of _scene_raw.objects) {
+	for (let i: i32 = 0; i < _scene_raw.objects.length; ++i) {
+		let o: obj_t = _scene_raw.objects[i];
 		if (o.type == "camera_object") {
 			mat4_set_from_f32_array(cam.base.transform.local, o.transform);
 			transform_decompose(cam.base.transform);