2
0
luboslenco 1 жил өмнө
parent
commit
a87a79141b
45 өөрчлөгдсөн 1784 нэмэгдсэн , 1871 устгасан
  1. 6 7
      armorforge/Sources/TabObjects.ts
  2. 6 8
      armorlab/Sources/MakeMaterial.ts
  3. 62 63
      armorlab/Sources/nodes/InpaintNode.ts
  4. 74 75
      armorlab/Sources/nodes/PhotoToPBRNode.ts
  5. 9 12
      armorlab/Sources/nodes/TextToPhotoNode.ts
  6. 3 4
      armorlab/Sources/nodes/UpscaleNode.ts
  7. 22 23
      armorlab/Sources/nodes/VarianceNode.ts
  8. 16 23
      armorpaint/Sources/MakeMaterial.ts
  9. 4 5
      armorpaint/Sources/RenderPathPaint.ts
  10. 2 3
      armorpaint/Sources/SlotBrush.ts
  11. 2 3
      armorpaint/Sources/SlotMaterial.ts
  12. 68 70
      armorsculpt/Sources/ImportMesh.ts
  13. 18 24
      armorsculpt/Sources/MakeMaterial.ts
  14. 3 4
      base/Sources/Args.ts
  15. 74 77
      base/Sources/Base.ts
  16. 3 4
      base/Sources/BoxExport.ts
  17. 12 15
      base/Sources/BoxPreferences.ts
  18. 6 8
      base/Sources/BoxProjects.ts
  19. 30 37
      base/Sources/Config.ts
  20. 326 341
      base/Sources/ImportArm.ts
  21. 214 215
      base/Sources/ImportBlendMaterial.ts
  22. 354 355
      base/Sources/ImportBlendMesh.ts
  23. 20 21
      base/Sources/ImportFont.ts
  24. 39 40
      base/Sources/ImportGpl.ts
  25. 65 67
      base/Sources/ImportMesh.ts
  26. 96 97
      base/Sources/ImportObj.ts
  27. 5 5
      base/Sources/ImportTexture.ts
  28. 1 1
      base/Sources/LineDraw.ts
  29. 5 6
      base/Sources/Plugin.ts
  30. 82 86
      base/Sources/Project.ts
  31. 2 2
      base/Sources/ProjectFormat.ts
  32. 9 10
      base/Sources/RenderPathRaytrace.ts
  33. 4 5
      base/Sources/Res.ts
  34. 2 1
      base/Sources/TabConsole.ts
  35. 1 2
      base/Sources/TabMeshes.ts
  36. 11 10
      base/Sources/TabScript.ts
  37. 15 16
      base/Sources/Translator.ts
  38. 33 38
      base/Sources/UIBase.ts
  39. 34 36
      base/Sources/UIFiles.ts
  40. 1 1
      base/Sources/UIHeader.ts
  41. 3 4
      base/Sources/UIMenu.ts
  42. 1 2
      base/Sources/UIMenubar.ts
  43. 5 6
      base/Sources/UtilMesh.ts
  44. 21 23
      base/Sources/UtilParticle.ts
  45. 15 16
      base/Sources/main.ts

+ 6 - 7
armorforge/Sources/TabObjects.ts

@@ -95,11 +95,10 @@ class TabObjects {
 									}
 								}
 
-								data_get_material("Scene", "TempMaterial" + TabObjects.materialId, (md: material_data_t) => {
-									let mo: mesh_object_t = currentObject.ext;
-									mo.materials = [md];
-									MakeMaterial.parseMeshPreviewMaterial(md);
-								});
+								let md: material_data_t = data_get_material("Scene", "TempMaterial" + TabObjects.materialId);
+								let mo: mesh_object_t = currentObject.ext;
+								mo.materials = [md];
+								MakeMaterial.parseMeshPreviewMaterial(md);
 							}
 						}, 1);
 					}
@@ -230,13 +229,13 @@ class TabObjects {
 						let p = scene_world;
 						p.strength = zui_slider(zui_handle("tabobjects_16", {value: p.strength}), "Environment", 0.0, 5.0, true);
 					}
-					else if (Context.raw.selectedObject.ext.constructor == light_object_t) {
+					else if (Context.raw.selectedObject.ext_type == "light_object_t") {
 						let light = Context.raw.selectedObject.ext;
 						let lightHandle = zui_handle("tabobjects_17");
 						lightHandle.value = light.data.strength / 10;
 						light.data.strength = zui_slider(lightHandle, "Strength", 0.0, 5.0, true) * 10;
 					}
-					else if (Context.raw.selectedObject.ext.constructor == camera_object_t) {
+					else if (Context.raw.selectedObject.ext_type == "camera_object_t") {
 						let cam = Context.raw.selectedObject.ext;
 						let fovHandle = zui_handle("tabobjects_18");
 						fovHandle.value = Math.floor(cam.data.fov * 100) / 100;

+ 6 - 8
armorlab/Sources/MakeMaterial.ts

@@ -18,8 +18,7 @@ class MakeMaterial {
 		}
 
 		let con = MakeMesh.run({ name: "Material", canvas: null });
-		let scon: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => { scon = _scon; });
+		let scon: shader_context_t = shader_context_create(con.data);
 		scon._override_context = {};
 		if (con.frag.sharedSamplers.length > 0) {
 			let sampler = con.frag.sharedSamplers[0];
@@ -85,15 +84,14 @@ class MakeMaterial {
 
 		let compileError = false;
 		let scon2: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon2 = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon2 = _scon;
+
 		if (compileError) return;
 		scon2._override_context = {};
 		scon2._override_context.addressing = "repeat";
-		let mcon3: material_context_t;
-		material_context_create(mcon2, (_mcon: material_context_t) => { mcon3 = _mcon; });
+		let mcon3: material_context_t = material_context_create(mcon2);
 
 		m._shader.contexts.push(scon2);
 		m._shader._contexts.push(scon2);

+ 62 - 63
armorlab/Sources/nodes/InpaintNode.ts

@@ -106,73 +106,72 @@ class InpaintNode extends LogicNode {
 		let u8 = new Uint8Array(bytes_img);
 		let f32mask = new Float32Array(4 * 64 * 64);
 
-		data_get_blob("models/sd_vae_encoder.quant.onnx", (vae_encoder_blob: ArrayBuffer) => {
-			// for (let x = 0; x < Math.floor(image.width / 512); ++x) {
-				// for (let y = 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) {
-							// 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);
-							let j = (yy * step * mask.width + xx * step);
-							let f = u8[j] / 255.0;
-							let i = yy * 64 + xx;
-							f32mask[i              ] = f;
-							f32mask[i + 64 * 64    ] = f;
-							f32mask[i + 64 * 64 * 2] = f;
-							f32mask[i + 64 * 64 * 3] = f;
-						}
+		let vae_encoder_blob: ArrayBuffer = 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) {
+				let x = 0;
+				let y = 0;
+
+				for (let xx = 0; xx < 64; ++xx) {
+					for (let yy = 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);
+						let j = (yy * step * mask.width + xx * step);
+						let f = u8[j] / 255.0;
+						let i = yy * 64 + xx;
+						f32mask[i              ] = f;
+						f32mask[i + 64 * 64    ] = f;
+						f32mask[i + 64 * 64 * 2] = f;
+						f32mask[i + 64 * 64 * 3] = f;
 					}
+				}
 
-					g2_begin(InpaintNode.temp, false);
-					// g2_drawImage(image, -x * 512, -y * 512);
-					g2_draw_scaled_image(image, 0, 0, 512, 512);
-					g2_end();
-
-					let bytes_img = image_get_pixels(InpaintNode.temp);
-					let u8a = new Uint8Array(bytes_img);
-					let f32a = new Float32Array(3 * 512 * 512);
-					for (let i = 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;
-					}
-
-					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 Float32Array(latents_buf);
-					for (let i = 0; i < latents.length; ++i) {
-						latents[i] = 0.18215 * latents[i];
-					}
-					let latents_orig = latents.slice(0);
-
-					let noise = new Float32Array(latents.length);
-					for (let i = 0; i < noise.length; ++i) noise[i] = Math.cos(2.0 * 3.14 * RandomNode.getFloat()) * Math.sqrt(-2.0 * Math.log(RandomNode.getFloat()));
-
-					let num_inference_steps = 50;
-					let init_timestep = Math.floor(num_inference_steps * InpaintNode.strength);
-					let timestep = TextToPhotoNode.timesteps[num_inference_steps - init_timestep];
-					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) {
-						latents[i] = sqrt_alpha_prod * latents[i] + sqrt_one_minus_alpha_prod * noise[i];
-					}
-
-					let start = num_inference_steps - init_timestep;
+				g2_begin(InpaintNode.temp, false);
+				// g2_drawImage(image, -x * 512, -y * 512);
+				g2_draw_scaled_image(image, 0, 0, 512, 512);
+				g2_end();
 
-					TextToPhotoNode.stableDiffusion(InpaintNode.prompt, (img: image_t) => {
-						// result.g2_begin(false);
-						// result.g2_draw_image(img, x * 512, y * 512);
-						// result.g2_end();
-						InpaintNode.result = img;
-						done(img);
-					}, latents, start, true, f32mask, latents_orig);
-				// }
+				bytes_img = image_get_pixels(InpaintNode.temp);
+				let u8a = new Uint8Array(bytes_img);
+				let f32a = new Float32Array(3 * 512 * 512);
+				for (let i = 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;
+				}
+
+				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 Float32Array(latents_buf);
+				for (let i = 0; i < latents.length; ++i) {
+					latents[i] = 0.18215 * latents[i];
+				}
+				let latents_orig = latents.slice(0);
+
+				let noise = new Float32Array(latents.length);
+				for (let i = 0; i < noise.length; ++i) noise[i] = Math.cos(2.0 * 3.14 * RandomNode.getFloat()) * Math.sqrt(-2.0 * Math.log(RandomNode.getFloat()));
+
+				let num_inference_steps = 50;
+				let init_timestep = Math.floor(num_inference_steps * InpaintNode.strength);
+				let timestep = TextToPhotoNode.timesteps[num_inference_steps - init_timestep];
+				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) {
+					latents[i] = sqrt_alpha_prod * latents[i] + sqrt_one_minus_alpha_prod * noise[i];
+				}
+
+				let start = num_inference_steps - init_timestep;
+
+				TextToPhotoNode.stableDiffusion(InpaintNode.prompt, (img: image_t) => {
+					// result.g2_begin(false);
+					// result.g2_draw_image(img, x * 512, y * 512);
+					// result.g2_end();
+					InpaintNode.result = img;
+					done(img);
+				}, latents, start, true, f32mask, latents_orig);
 			// }
-		});
+		// }
 	}
 
 	static def: zui_node_t = {

+ 74 - 75
armorlab/Sources/nodes/PhotoToPBRNode.ts

@@ -67,87 +67,86 @@ class PhotoToPBRNode extends LogicNode {
 						f32a[i + PhotoToPBRNode.tileWithBorderW * PhotoToPBRNode.tileWithBorderW * 2] = (u8a[i * 4 + 2] / 255 - 0.5) / 0.5;
 					}
 
-					data_get_blob("models/photo_to_" + PhotoToPBRNode.modelNames[from] + ".quant.onnx", (model_blob: ArrayBuffer) => {
-						let buf = krom_ml_inference(model_blob, [f32a.buffer], null, null, Config.raw.gpu_inference);
-						let ar = new Float32Array(buf);
-						let u8a = new Uint8Array(4 * PhotoToPBRNode.tileW * PhotoToPBRNode.tileW);
-						let offsetG = (from == ChannelType.ChannelBaseColor || from == ChannelType.ChannelNormalMap) ? PhotoToPBRNode.tileWithBorderW * PhotoToPBRNode.tileWithBorderW : 0;
-						let offsetB = (from == ChannelType.ChannelBaseColor || from == ChannelType.ChannelNormalMap) ? PhotoToPBRNode.tileWithBorderW * PhotoToPBRNode.tileWithBorderW * 2 : 0;
-						for (let i = 0; i < (PhotoToPBRNode.tileW * PhotoToPBRNode.tileW); ++i) {
-							let x = PhotoToPBRNode.borderW + i % PhotoToPBRNode.tileW;
-							let y = PhotoToPBRNode.borderW + Math.floor(i / PhotoToPBRNode.tileW);
-							u8a[i * 4    ] = Math.floor((ar[y * PhotoToPBRNode.tileWithBorderW + x          ] * 0.5 + 0.5) * 255);
-							u8a[i * 4 + 1] = Math.floor((ar[y * PhotoToPBRNode.tileWithBorderW + x + offsetG] * 0.5 + 0.5) * 255);
-							u8a[i * 4 + 2] = Math.floor((ar[y * PhotoToPBRNode.tileWithBorderW + x + offsetB] * 0.5 + 0.5) * 255);
-							u8a[i * 4 + 3] = 255;
-						}
-						tileFloats.push(ar);
-
-						// Use border pixels to blend seams
-						if (i > 0) {
-							if (x > 0) {
-								let ar = tileFloats[i - 1];
-								for (let yy = 0; yy < PhotoToPBRNode.tileW; ++yy) {
-									for (let xx = 0; xx < PhotoToPBRNode.borderW; ++xx) {
-										let i = yy * PhotoToPBRNode.tileW + xx;
-										let a = u8a[i * 4];
-										let b = u8a[i * 4 + 1];
-										let c = u8a[i * 4 + 2];
-
-										let aa = Math.floor((ar[(PhotoToPBRNode.borderW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + xx          ] * 0.5 + 0.5) * 255);
-										let bb = Math.floor((ar[(PhotoToPBRNode.borderW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + xx + offsetG] * 0.5 + 0.5) * 255);
-										let cc = Math.floor((ar[(PhotoToPBRNode.borderW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + xx + offsetB] * 0.5 + 0.5) * 255);
-
-										let f = xx / PhotoToPBRNode.borderW;
-										let invf = 1.0 - f;
-										a = Math.floor(a * f + aa * invf);
-										b = Math.floor(b * f + bb * invf);
-										c = Math.floor(c * f + cc * invf);
-
-										u8a[i * 4    ] = a;
-										u8a[i * 4 + 1] = b;
-										u8a[i * 4 + 2] = c;
-									}
+					let model_blob: ArrayBuffer = data_get_blob("models/photo_to_" + PhotoToPBRNode.modelNames[from] + ".quant.onnx");
+					let buf = krom_ml_inference(model_blob, [f32a.buffer], null, null, Config.raw.gpu_inference);
+					let ar = new Float32Array(buf);
+					u8a = new Uint8Array(4 * PhotoToPBRNode.tileW * PhotoToPBRNode.tileW);
+					let offsetG = (from == ChannelType.ChannelBaseColor || from == ChannelType.ChannelNormalMap) ? PhotoToPBRNode.tileWithBorderW * PhotoToPBRNode.tileWithBorderW : 0;
+					let offsetB = (from == ChannelType.ChannelBaseColor || from == ChannelType.ChannelNormalMap) ? PhotoToPBRNode.tileWithBorderW * PhotoToPBRNode.tileWithBorderW * 2 : 0;
+					for (let i = 0; i < (PhotoToPBRNode.tileW * PhotoToPBRNode.tileW); ++i) {
+						let x = PhotoToPBRNode.borderW + i % PhotoToPBRNode.tileW;
+						let y = PhotoToPBRNode.borderW + Math.floor(i / PhotoToPBRNode.tileW);
+						u8a[i * 4    ] = Math.floor((ar[y * PhotoToPBRNode.tileWithBorderW + x          ] * 0.5 + 0.5) * 255);
+						u8a[i * 4 + 1] = Math.floor((ar[y * PhotoToPBRNode.tileWithBorderW + x + offsetG] * 0.5 + 0.5) * 255);
+						u8a[i * 4 + 2] = Math.floor((ar[y * PhotoToPBRNode.tileWithBorderW + x + offsetB] * 0.5 + 0.5) * 255);
+						u8a[i * 4 + 3] = 255;
+					}
+					tileFloats.push(ar);
+
+					// Use border pixels to blend seams
+					if (i > 0) {
+						if (x > 0) {
+							let ar = tileFloats[i - 1];
+							for (let yy = 0; yy < PhotoToPBRNode.tileW; ++yy) {
+								for (let xx = 0; xx < PhotoToPBRNode.borderW; ++xx) {
+									let i = yy * PhotoToPBRNode.tileW + xx;
+									let a = u8a[i * 4];
+									let b = u8a[i * 4 + 1];
+									let c = u8a[i * 4 + 2];
+
+									let aa = Math.floor((ar[(PhotoToPBRNode.borderW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + xx          ] * 0.5 + 0.5) * 255);
+									let bb = Math.floor((ar[(PhotoToPBRNode.borderW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + xx + offsetG] * 0.5 + 0.5) * 255);
+									let cc = Math.floor((ar[(PhotoToPBRNode.borderW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + xx + offsetB] * 0.5 + 0.5) * 255);
+
+									let f = xx / PhotoToPBRNode.borderW;
+									let invf = 1.0 - f;
+									a = Math.floor(a * f + aa * invf);
+									b = Math.floor(b * f + bb * invf);
+									c = Math.floor(c * f + cc * invf);
+
+									u8a[i * 4    ] = a;
+									u8a[i * 4 + 1] = b;
+									u8a[i * 4 + 2] = c;
 								}
 							}
-							if (y > 0) {
-								let ar = tileFloats[i - tilesX];
-								for (let xx = 0; xx < PhotoToPBRNode.tileW; ++xx) {
-									for (let yy = 0; yy < PhotoToPBRNode.borderW; ++yy) {
-										let i = yy * PhotoToPBRNode.tileW + xx;
-										let a = u8a[i * 4];
-										let b = u8a[i * 4 + 1];
-										let c = u8a[i * 4 + 2];
-
-										let aa = Math.floor((ar[(PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + xx          ] * 0.5 + 0.5) * 255);
-										let bb = Math.floor((ar[(PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + xx + offsetG] * 0.5 + 0.5) * 255);
-										let cc = Math.floor((ar[(PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + xx + offsetB] * 0.5 + 0.5) * 255);
-
-										let f = yy / PhotoToPBRNode.borderW;
-										let invf = 1.0 - f;
-										a = Math.floor(a * f + aa * invf);
-										b = Math.floor(b * f + bb * invf);
-										c = Math.floor(c * f + cc * invf);
-
-										u8a[i * 4    ] = a;
-										u8a[i * 4 + 1] = b;
-										u8a[i * 4 + 2] = c;
-									}
+						}
+						if (y > 0) {
+							let ar = tileFloats[i - tilesX];
+							for (let xx = 0; xx < PhotoToPBRNode.tileW; ++xx) {
+								for (let yy = 0; yy < PhotoToPBRNode.borderW; ++yy) {
+									let i = yy * PhotoToPBRNode.tileW + xx;
+									let a = u8a[i * 4];
+									let b = u8a[i * 4 + 1];
+									let c = u8a[i * 4 + 2];
+
+									let aa = Math.floor((ar[(PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + xx          ] * 0.5 + 0.5) * 255);
+									let bb = Math.floor((ar[(PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + xx + offsetG] * 0.5 + 0.5) * 255);
+									let cc = Math.floor((ar[(PhotoToPBRNode.borderW + PhotoToPBRNode.tileW + yy) * PhotoToPBRNode.tileWithBorderW + PhotoToPBRNode.borderW + xx + offsetB] * 0.5 + 0.5) * 255);
+
+									let f = yy / PhotoToPBRNode.borderW;
+									let invf = 1.0 - f;
+									a = Math.floor(a * f + aa * invf);
+									b = Math.floor(b * f + bb * invf);
+									c = Math.floor(c * f + cc * invf);
+
+									u8a[i * 4    ] = a;
+									u8a[i * 4 + 1] = b;
+									u8a[i * 4 + 2] = c;
 								}
 							}
 						}
+					}
+
+					///if (krom_metal || krom_vulkan)
+					if (from == ChannelType.ChannelBaseColor) PhotoToPBRNode.bgraSwap(u8a.buffer);
+					///end
 
-						///if (krom_metal || krom_vulkan)
-						if (from == ChannelType.ChannelBaseColor) PhotoToPBRNode.bgraSwap(u8a.buffer);
-						///end
-
-						let temp2 = image_from_bytes(u8a.buffer, PhotoToPBRNode.tileW, PhotoToPBRNode.tileW);
-						g2_begin(PhotoToPBRNode.images[from], false);
-						g2_draw_image(temp2, x * PhotoToPBRNode.tileW, y * PhotoToPBRNode.tileW);
-						g2_end();
-						Base.notifyOnNextFrame(() => {
-							image_unload(temp2);
-						});
+					let temp2 = image_from_bytes(u8a.buffer, PhotoToPBRNode.tileW, PhotoToPBRNode.tileW);
+					g2_begin(PhotoToPBRNode.images[from], false);
+					g2_draw_image(temp2, x * PhotoToPBRNode.tileW, y * PhotoToPBRNode.tileW);
+					g2_end();
+					Base.notifyOnNextFrame(() => {
+						image_unload(temp2);
 					});
 				}
 

+ 9 - 12
armorlab/Sources/nodes/TextToPhotoNode.ts

@@ -30,20 +30,17 @@ class TextToPhotoNode extends LogicNode {
 	}
 
 	static stableDiffusion = (prompt: string, done: (img: image_t)=>void, inpaintLatents: Float32Array = null, offset = 0, upscale = true, mask: Float32Array = null, latents_orig: Float32Array = null) => {
-		data_get_blob("models/sd_text_encoder.quant.onnx", (_text_encoder_blob: ArrayBuffer) => {
-		data_get_blob("models/sd_unet.quant.onnx", (_unet_blob: ArrayBuffer) => {
-		data_get_blob("models/sd_vae_decoder.quant.onnx", (_vae_decoder_blob: ArrayBuffer) => {
-			TextToPhotoNode.text_encoder_blob = _text_encoder_blob;
-			TextToPhotoNode.unet_blob = _unet_blob;
-			TextToPhotoNode.vae_decoder_blob = _vae_decoder_blob;
-			TextToPhotoNode.textEncoder(prompt, inpaintLatents, (latents: Float32Array, text_embeddings: Float32Array) => {
-				TextToPhotoNode.unet(latents, text_embeddings, mask, latents_orig, offset, (latents: Float32Array) => {
-					TextToPhotoNode.vaeDecoder(latents, upscale, done);
-				});
+		let _text_encoder_blob: ArrayBuffer = data_get_blob("models/sd_text_encoder.quant.onnx");
+		let _unet_blob: ArrayBuffer = data_get_blob("models/sd_unet.quant.onnx");
+		let _vae_decoder_blob: ArrayBuffer = data_get_blob("models/sd_vae_decoder.quant.onnx");
+		TextToPhotoNode.text_encoder_blob = _text_encoder_blob;
+		TextToPhotoNode.unet_blob = _unet_blob;
+		TextToPhotoNode.vae_decoder_blob = _vae_decoder_blob;
+		TextToPhotoNode.textEncoder(prompt, inpaintLatents, (latents: Float32Array, text_embeddings: Float32Array) => {
+			TextToPhotoNode.unet(latents, text_embeddings, mask, latents_orig, offset, (latents: Float32Array) => {
+				TextToPhotoNode.vaeDecoder(latents, upscale, done);
 			});
 		});
-		});
-		});
 	}
 
 	static textEncoder = (prompt: string, inpaintLatents: Float32Array, done: (a: Float32Array, b: Float32Array)=>void) => {

+ 3 - 4
armorlab/Sources/nodes/UpscaleNode.ts

@@ -31,10 +31,9 @@ class UpscaleNode extends LogicNode {
 	}
 
 	static loadBlob = (done: ()=>void) => {
-		data_get_blob("models/esrgan.quant.onnx", (_esrgan_blob: ArrayBuffer) => {
-			UpscaleNode.esrgan_blob = _esrgan_blob;
-			done();
-		});
+		let _esrgan_blob: ArrayBuffer = data_get_blob("models/esrgan.quant.onnx");
+		UpscaleNode.esrgan_blob = _esrgan_blob;
+		done();
 	}
 
 	override getCachedImage = (): image_t => {

+ 22 - 23
armorlab/Sources/nodes/VarianceNode.ts

@@ -42,31 +42,30 @@ class VarianceNode extends LogicNode {
 
 			Console.progress(tr("Processing") + " - " + tr("Variance"));
 			Base.notifyOnNextFrame(() => {
-				data_get_blob("models/sd_vae_encoder.quant.onnx", (vae_encoder_blob: ArrayBuffer) => {
-					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 Float32Array(latents_buf);
-					for (let i = 0; i < latents.length; ++i) {
-						latents[i] = 0.18215 * latents[i];
-					}
+				let vae_encoder_blob: ArrayBuffer = 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 Float32Array(latents_buf);
+				for (let i = 0; i < latents.length; ++i) {
+					latents[i] = 0.18215 * latents[i];
+				}
 
-					let noise = new Float32Array(latents.length);
-					for (let i = 0; i < noise.length; ++i) noise[i] = Math.cos(2.0 * 3.14 * RandomNode.getFloat()) * Math.sqrt(-2.0 * Math.log(RandomNode.getFloat()));
-					let num_inference_steps = 50;
-					let init_timestep = Math.floor(num_inference_steps * strength);
-					let timesteps = TextToPhotoNode.timesteps[num_inference_steps - init_timestep];
-					let alphas_cumprod = TextToPhotoNode.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) {
-						latents[i] = sqrt_alpha_prod * latents[i] + sqrt_one_minus_alpha_prod * noise[i];
-					}
-					let t_start = num_inference_steps - init_timestep;
+				let noise = new Float32Array(latents.length);
+				for (let i = 0; i < noise.length; ++i) noise[i] = Math.cos(2.0 * 3.14 * RandomNode.getFloat()) * Math.sqrt(-2.0 * Math.log(RandomNode.getFloat()));
+				let num_inference_steps = 50;
+				let init_timestep = Math.floor(num_inference_steps * strength);
+				let timesteps = TextToPhotoNode.timesteps[num_inference_steps - init_timestep];
+				let alphas_cumprod = TextToPhotoNode.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) {
+					latents[i] = sqrt_alpha_prod * latents[i] + sqrt_one_minus_alpha_prod * noise[i];
+				}
+				let t_start = num_inference_steps - init_timestep;
 
-					TextToPhotoNode.stableDiffusion(VarianceNode.prompt, (_image: image_t) => {
-						VarianceNode.image = _image;
-						done(VarianceNode.image);
-					}, latents, t_start);
-				});
+				TextToPhotoNode.stableDiffusion(VarianceNode.prompt, (_image: image_t) => {
+					VarianceNode.image = _image;
+					done(VarianceNode.image);
+				}, latents, t_start);
 			});
 		});
 	}

+ 16 - 23
armorpaint/Sources/MakeMaterial.ts

@@ -57,8 +57,7 @@ class MakeMaterial {
 		}
 
 		let con = MakeMesh.run({ name: "Material", canvas: null });
-		let scon: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => { scon = _scon; });
+		let scon: shader_context_t = shader_context_create(con.data);
 		scon._override_context = {};
 		if (con.frag.sharedSamplers.length > 0) {
 			let sampler = con.frag.sharedSamplers[0];
@@ -72,8 +71,7 @@ class MakeMaterial {
 
 		for (let i = 1; i < MakeMesh.layerPassCount; ++i) {
 			let con = MakeMesh.run({ name: "Material", canvas: null }, i);
-			let scon: shader_context_t;
-			shader_context_create(con.data, (_scon: shader_context_t) => { scon = _scon; });
+			let scon: shader_context_t = shader_context_create(con.data);
 			scon._override_context = {};
 			if (con.frag.sharedSamplers.length > 0) {
 				let sampler = con.frag.sharedSamplers[0];
@@ -86,7 +84,7 @@ class MakeMaterial {
 			m._shader._contexts.push(scon);
 
 			let mcon: material_context_t;
-			material_context_create({ name: "mesh" + i, bind_textures: [] }, (self: material_context_t) => { mcon = self; });
+			mcon = material_context_create({ name: "mesh" + i, bind_textures: [] });
 			m.contexts.push(mcon);
 			m._contexts.push(mcon);
 		}
@@ -117,7 +115,7 @@ class MakeMaterial {
 		}
 		let con = MakeParticle.run({ name: "MaterialParticle", canvas: null });
 		if (sc != null) MakeMaterial.deleteContext(sc);
-		shader_context_create(con.data, (_sc: shader_context_t) => { sc = _sc; });
+		sc = shader_context_create(con.data);
 		m._shader.contexts.push(sc);
 		m._shader._contexts.push(sc);
 	}
@@ -144,7 +142,7 @@ class MakeMaterial {
 
 		for (let i = 0; i < m._contexts.length; ++i) {
 			if (m._contexts[i].name == "mesh") {
-				material_context_create(mcon, (self: material_context_t) => { m._contexts[i] = self; });
+				m._contexts[i] = material_context_create(mcon);
 				break;
 			}
 		}
@@ -152,10 +150,9 @@ class MakeMaterial {
 		if (scon != null) MakeMaterial.deleteContext(scon);
 
 		let compileError = false;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon = _scon;
 		if (compileError) return;
 
 		m._shader.contexts.push(scon);
@@ -213,15 +210,13 @@ class MakeMaterial {
 
 		let compileError = false;
 		let scon: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon = _scon;
 		if (compileError) return;
 		scon._override_context = {};
 		scon._override_context.addressing = "repeat";
-		let mcon: material_context_t;
-		material_context_create(tmcon, (_mcon: material_context_t) => { mcon = _mcon; });
+		let mcon: material_context_t = material_context_create(tmcon);
 
 		m._shader.contexts.push(scon);
 		m._shader._contexts.push(scon);
@@ -354,13 +349,11 @@ class MakeMaterial {
 		let con = MakeNodePreview.run(sdata, mcon_raw, node, group, parents);
 		let compileError = false;
 		let scon: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon = _scon;
 		if (compileError) return null;
-		let mcon: material_context_t;
-		material_context_create(mcon_raw, (_mcon: material_context_t) => { mcon = _mcon; });
+		let mcon: material_context_t = material_context_create(mcon_raw);
 		return { scon: scon, mcon: mcon };
 	}
 

+ 4 - 5
armorpaint/Sources/RenderPathPaint.ts

@@ -833,11 +833,10 @@ class RenderPathPaint {
 				scale_pos: 1.5,
 				scale_tex: 1.0
 			};
-			mesh_data_create(raw, (md: mesh_data_t) => {
-				let materials: material_data_t[] = scene_get_child(".Plane").ext.materials;
-				let o = scene_add_mesh_object(md, materials);
-				o.base.name = ".PlaneTiled";
-			});
+			let md: mesh_data_t = mesh_data_create(raw);
+			let materials: material_data_t[] = scene_get_child(".Plane").ext.materials;
+			let o = scene_add_mesh_object(md, materials);
+			o.base.name = ".PlaneTiled";
 		}
 
 		RenderPathPaint.planeo = scene_get_child(tiled ? ".PlaneTiled" : ".Plane").ext;

+ 2 - 3
armorpaint/Sources/SlotBrush.ts

@@ -17,9 +17,8 @@ class SlotBrush {
 
 		if (c == null) {
 			if (SlotBrush.defaultCanvas == null) { // Synchronous
-				data_get_blob("default_brush.arm", (b: ArrayBuffer) => {
-					SlotBrush.defaultCanvas = b;
-				});
+				let b: ArrayBuffer = data_get_blob("default_brush.arm")
+				SlotBrush.defaultCanvas = b;
 			}
 			raw.canvas = armpack_decode(SlotBrush.defaultCanvas);
 			raw.canvas.name = "Brush " + (raw.id + 1);

+ 2 - 3
armorpaint/Sources/SlotMaterial.ts

@@ -34,9 +34,8 @@ class SlotMaterial {
 
 		if (c == null) {
 			if (SlotMaterial.defaultCanvas == null) { // Synchronous
-				data_get_blob("default_material.arm", (b: ArrayBuffer) => {
-					SlotMaterial.defaultCanvas = b;
-				});
+				let b: ArrayBuffer = data_get_blob("default_material.arm");
+				SlotMaterial.defaultCanvas = b;
 			}
 			raw.canvas = armpack_decode(SlotMaterial.defaultCanvas);
 			raw.canvas.name = "Material " + (raw.id + 1);

+ 68 - 70
armorsculpt/Sources/ImportMesh.ts

@@ -79,62 +79,61 @@ class ImportMesh {
 			let raw = ImportMesh.rawMesh(mesh);
 			if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 });
 
-			mesh_data_create(raw, (md: mesh_data_t) => {
-				Context.raw.paintObject = Context.mainObject();
-
-				Context.selectPaintObject(Context.mainObject());
-				for (let i = 0; i < Project.paintObjects.length; ++i) {
-					let p = Project.paintObjects[i];
-					if (p == Context.raw.paintObject) continue;
-					data_delete_mesh(p.data._handle);
-					mesh_object_remove(p);
-				}
-				let handle = Context.raw.paintObject.data._handle;
-				if (handle != "SceneSphere" && handle != "ScenePlane") {
-					data_delete_mesh(handle);
-				}
+			let md: mesh_data_t = mesh_data_create(raw);
+			Context.raw.paintObject = Context.mainObject();
+
+			Context.selectPaintObject(Context.mainObject());
+			for (let i = 0; i < Project.paintObjects.length; ++i) {
+				let p = Project.paintObjects[i];
+				if (p == Context.raw.paintObject) continue;
+				data_delete_mesh(p.data._handle);
+				mesh_object_remove(p);
+			}
+			let handle = Context.raw.paintObject.data._handle;
+			if (handle != "SceneSphere" && handle != "ScenePlane") {
+				data_delete_mesh(handle);
+			}
 
-				if (ImportMesh.clearLayers) {
-					while (Project.layers.length > 0) {
-						let l = Project.layers.pop();
-						SlotLayer.unload(l);
-					}
-					Base.newLayer(false);
-					app_notify_on_init(Base.initLayers);
-					History.reset();
+			if (ImportMesh.clearLayers) {
+				while (Project.layers.length > 0) {
+					let l = Project.layers.pop();
+					SlotLayer.unload(l);
 				}
+				Base.newLayer(false);
+				app_notify_on_init(Base.initLayers);
+				History.reset();
+			}
+
+			mesh_object_set_data(Context.raw.paintObject, md);
+			Context.raw.paintObject.base.name = mesh.name;
+			Project.paintObjects = [Context.raw.paintObject];
 
-				mesh_object_set_data(Context.raw.paintObject, md);
-				Context.raw.paintObject.base.name = mesh.name;
-				Project.paintObjects = [Context.raw.paintObject];
-
-				md._handle = raw.name;
-				data_cached_meshes.set(md._handle, md);
-
-				Context.raw.ddirty = 4;
-				UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
-				UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
-
-				// Wait for addMesh calls to finish
-				app_notify_on_init(ImportMesh.finishImport);
-
-				Base.notifyOnNextFrame(() => {
-					let f32 = new Float32Array(Config.getTextureResX() * Config.getTextureResY() * 4);
-					for (let i = 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;
-						f32[i * 4 + 2] = mesh.posa[index * 4 + 2] / 32767;
-						f32[i * 4 + 3] = 1.0;
-					}
-					let imgmesh = image_from_bytes(f32.buffer, Config.getTextureResX(), Config.getTextureResY(), tex_format_t.RGBA128);
-					let texpaint = Project.layers[0].texpaint;
-					g2_begin(texpaint, false);
-					g2_set_pipeline(Base.pipeCopy128);
-					g2_draw_scaled_image(imgmesh, 0, 0, Config.getTextureResX(), Config.getTextureResY());
-					g2_set_pipeline(null);
-					g2_end();
-				});
+			md._handle = raw.name;
+			data_cached_meshes.set(md._handle, md);
+
+			Context.raw.ddirty = 4;
+			UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
+			UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
+
+			// Wait for addMesh calls to finish
+			app_notify_on_init(ImportMesh.finishImport);
+
+			Base.notifyOnNextFrame(() => {
+				let f32 = new Float32Array(Config.getTextureResX() * Config.getTextureResY() * 4);
+				for (let i = 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;
+					f32[i * 4 + 2] = mesh.posa[index * 4 + 2] / 32767;
+					f32[i * 4 + 3] = 1.0;
+				}
+				let imgmesh = image_from_bytes(f32.buffer, Config.getTextureResX(), Config.getTextureResY(), tex_format_t.RGBA128);
+				let texpaint = Project.layers[0].texpaint;
+				g2_begin(texpaint, false);
+				g2_set_pipeline(Base.pipeCopy128);
+				g2_draw_scaled_image(imgmesh, 0, 0, Config.getTextureResX(), Config.getTextureResY());
+				g2_set_pipeline(null);
+				g2_end();
 			});
 		}
 
@@ -147,29 +146,28 @@ class ImportMesh {
 			let raw = ImportMesh.rawMesh(mesh);
 			if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 });
 
-			mesh_data_create(raw, (md: mesh_data_t) => {
+			let md: mesh_data_t = mesh_data_create(raw);
 
-				let object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
-				object.base.name = mesh.base.name;
-				object.skip_context = "paint";
+			let object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
+			object.base.name = mesh.base.name;
+			object.skip_context = "paint";
 
-				// Ensure unique names
-				for (let p of Project.paintObjects) {
-					if (p.base.name == object.base.name) {
-						p.base.name += ".001";
-						p.data._handle += ".001";
-						data_cached_meshes.set(p.data._handle, p.data);
-					}
+			// Ensure unique names
+			for (let p of Project.paintObjects) {
+				if (p.base.name == object.base.name) {
+					p.base.name += ".001";
+					p.data._handle += ".001";
+					data_cached_meshes.set(p.data._handle, p.data);
 				}
+			}
 
-				Project.paintObjects.push(object);
+			Project.paintObjects.push(object);
 
-				md._handle = raw.name;
-				data_cached_meshes.set(md._handle, md);
+			md._handle = raw.name;
+			data_cached_meshes.set(md._handle, md);
 
-				Context.raw.ddirty = 4;
-				UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
-			});
+			Context.raw.ddirty = 4;
+			UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
 		}
 
 		_addMesh();

+ 18 - 24
armorsculpt/Sources/MakeMaterial.ts

@@ -57,8 +57,7 @@ class MakeMaterial {
 		}
 
 		let con = MakeMesh.run({ name: "Material", canvas: null });
-		let scon: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => { scon = _scon; });
+		let scon: shader_context_t = shader_context_create(con.data);
 		scon._override_context = {};
 		if (con.frag.sharedSamplers.length > 0) {
 			let sampler = con.frag.sharedSamplers[0];
@@ -72,8 +71,7 @@ class MakeMaterial {
 
 		for (let i = 1; i < MakeMesh.layerPassCount; ++i) {
 			let con = MakeMesh.run({ name: "Material", canvas: null }, i);
-			let scon: shader_context_t;
-			shader_context_create(con.data, (_scon: shader_context_t) => { scon = _scon; });
+			let scon: shader_context_t = shader_context_create(con.data);
 			scon._override_context = {};
 			if (con.frag.sharedSamplers.length > 0) {
 				let sampler = con.frag.sharedSamplers[0];
@@ -85,8 +83,7 @@ class MakeMaterial {
 			m._shader.contexts.push(scon);
 			m._shader._contexts.push(scon);
 
-			let mcon: material_context_t;
-			material_context_create({ name: "mesh" + i, bind_textures: [] }, (self: material_context_t) => { mcon = self; });
+			let mcon: material_context_t = material_context_create({ name: "mesh" + i, bind_textures: [] });
 			m.contexts.push(mcon);
 			m._contexts.push(mcon);
 		}
@@ -113,7 +110,7 @@ class MakeMaterial {
 		}
 		let con = MakeParticle.run({ name: "MaterialParticle", canvas: null });
 		if (sc != null) MakeMaterial.deleteContext(sc);
-		shader_context_create(con.data, (_sc: shader_context_t) => { sc = _sc; });
+		sc = shader_context_create(con.data);
 		m._shader.contexts.push(sc);
 		m._shader._contexts.push(sc);
 	}
@@ -139,7 +136,7 @@ class MakeMaterial {
 
 		for (let i = 0; i < m.contexts.length; ++i) {
 			if (m.contexts[i].name == "mesh") {
-				material_context_create(mcon, (self: material_context_t) => { m.contexts[i] = self; });
+				m.contexts[i] = material_context_create(mcon);
 				break;
 			}
 		}
@@ -147,10 +144,9 @@ class MakeMaterial {
 		if (scon != null) MakeMaterial.deleteContext(scon);
 
 		let compileError = false;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon = _scon;
 		if (compileError) return;
 
 		m._shader.contexts.push(scon);
@@ -208,15 +204,14 @@ class MakeMaterial {
 
 		let compileError = false;
 		let scon2: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon2 = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon2 = _scon;
+
 		if (compileError) return;
 		scon2._override_context = {};
 		scon2._override_context.addressing = "repeat";
-		let mcon3: material_context_t;
-		material_context_create(mcon2, (_mcon: material_context_t) => { mcon3 = _mcon; });
+		let mcon3: material_context_t = material_context_create(mcon2);
 
 		m._shader.contexts.push(scon2);
 		m._shader._contexts.push(scon2);
@@ -298,13 +293,12 @@ class MakeMaterial {
 		let con = MakeNodePreview.run(sdata, mcon_raw, node, group, parents);
 		let compileError = false;
 		let scon: shader_context_t;
-		shader_context_create(con.data, (_scon: shader_context_t) => {
-			if (_scon == null) compileError = true;
-			scon = _scon;
-		});
+		let _scon: shader_context_t = shader_context_create(con.data);
+		if (_scon == null) compileError = true;
+		scon = _scon;
+
 		if (compileError) return null;
-		let mcon: material_context_t;
-		material_context_create(mcon_raw, (_mcon: material_context_t) => { mcon = _mcon; });
+		let mcon: material_context_t = material_context_create(mcon_raw);
 		return { scon: scon, mcon: mcon };
 	}
 

+ 3 - 4
base/Sources/Args.ts

@@ -145,10 +145,9 @@ class Args {
 								file = "export_presets/" + BoxExport.files[BoxExport.files.indexOf(f)] + ".json";
 							}
 
-							data_get_blob(file, (blob: ArrayBuffer) => {
-								BoxExport.preset = JSON.parse(sys_buffer_to_string(blob));
-								data_delete_blob("export_presets/" + file);
-							});
+							let blob: ArrayBuffer = data_get_blob(file);
+							BoxExport.preset = JSON.parse(sys_buffer_to_string(blob));
+							data_delete_blob("export_presets/" + file);
 
 							// Export queue
 							app_notify_on_init(() => {

+ 74 - 77
base/Sources/Base.ts

@@ -138,93 +138,90 @@ class Base {
 
 		krom_set_save_and_quit_callback(Base.saveAndQuitCallback);
 
-		data_get_font("font.ttf", (f: g2_font_t) => {
-			data_get_image("color_wheel.k", (imageColorWheel: image_t) => {
-				data_get_image("color_wheel_gradient.k", (imageColorWheelGradient: image_t) => {
-
-					Base.font = f;
-					Config.loadTheme(Config.raw.theme, false);
-					Base.defaultElementW = Base.theme.ELEMENT_W;
-					Base.defaultFontSize = Base.theme.FONT_SIZE;
-					Translator.loadTranslations(Config.raw.locale);
-					UIFiles.filename = tr("untitled");
-					///if (krom_android || krom_ios)
-					sys_title_set(tr("untitled"));
-					///end
+		let f: g2_font_t = data_get_font("font.ttf");
+		let imageColorWheel: image_t = data_get_image("color_wheel.k");
+		let imageColorWheelGradient: image_t = data_get_image("color_wheel_gradient.k");
+
+		Base.font = f;
+		Config.loadTheme(Config.raw.theme, false);
+		Base.defaultElementW = Base.theme.ELEMENT_W;
+		Base.defaultFontSize = Base.theme.FONT_SIZE;
+		Translator.loadTranslations(Config.raw.locale);
+		UIFiles.filename = tr("untitled");
+		///if (krom_android || krom_ios)
+		sys_title_set(tr("untitled"));
+		///end
 
-					// Baked font for fast startup
-					if (Config.raw.locale == "en") {
-						Base.font.font_ = krom_g2_font_13(Base.font.blob);
-						Base.font.glyphs = _g2_font_glyphs;
-					}
-					else g2_font_init(Base.font);
-
-					Base.colorWheel = imageColorWheel;
-					Base.colorWheelGradient = imageColorWheelGradient;
-					zui_set_enum_texts(Base.enumTexts);
-					zui_tr = tr;
-					Base.uiBox = zui_create({ theme: Base.theme, font: f, scaleFactor: Config.raw.window_scale, color_wheel: Base.colorWheel, black_white_gradient: Base.colorWheelGradient });
-					Base.uiMenu = zui_create({ theme: Base.theme, font: f, scaleFactor: Config.raw.window_scale, color_wheel: Base.colorWheel, black_white_gradient: Base.colorWheelGradient });
-					Base.defaultElementH = Base.uiMenu.t.ELEMENT_H;
-
-					// Init plugins
-					if (Config.raw.plugins != null) {
-						for (let plugin of Config.raw.plugins) {
-							Plugin.start(plugin);
-						}
-					}
+		// Baked font for fast startup
+		if (Config.raw.locale == "en") {
+			Base.font.font_ = krom_g2_font_13(Base.font.blob);
+			Base.font.glyphs = _g2_font_glyphs;
+		}
+		else g2_font_init(Base.font);
+
+		Base.colorWheel = imageColorWheel;
+		Base.colorWheelGradient = imageColorWheelGradient;
+		zui_set_enum_texts(Base.enumTexts);
+		zui_tr = tr;
+		Base.uiBox = zui_create({ theme: Base.theme, font: f, scaleFactor: Config.raw.window_scale, color_wheel: Base.colorWheel, black_white_gradient: Base.colorWheelGradient });
+		Base.uiMenu = zui_create({ theme: Base.theme, font: f, scaleFactor: Config.raw.window_scale, color_wheel: Base.colorWheel, black_white_gradient: Base.colorWheelGradient });
+		Base.defaultElementH = Base.uiMenu.t.ELEMENT_H;
+
+		// Init plugins
+		if (Config.raw.plugins != null) {
+			for (let plugin of Config.raw.plugins) {
+				Plugin.start(plugin);
+			}
+		}
 
-					Args.parse();
+		Args.parse();
 
-					new Camera();
-					new UIBase();
-					new UINodes();
-					new UIView2D();
+		new Camera();
+		new UIBase();
+		new UINodes();
+		new UIView2D();
 
-					///if is_lab
-					RandomNode.setSeed(Math.floor(time_time() * 4294967295));
-					///end
+		///if is_lab
+		RandomNode.setSeed(Math.floor(time_time() * 4294967295));
+		///end
 
-					app_notify_on_update(Base.update);
-					app_notify_on_render_2d(UIView2D.render);
-					app_notify_on_update(UIView2D.update);
-					///if (is_paint || is_sculpt)
-					app_notify_on_render_2d(UIBase.renderCursor);
-					///end
-					app_notify_on_update(UINodes.update);
-					app_notify_on_render_2d(UINodes.render);
-					app_notify_on_update(UIBase.update);
-					app_notify_on_render_2d(UIBase.render);
-					app_notify_on_update(Camera.update);
-					app_notify_on_render_2d(Base.render);
+		app_notify_on_update(Base.update);
+		app_notify_on_render_2d(UIView2D.render);
+		app_notify_on_update(UIView2D.update);
+		///if (is_paint || is_sculpt)
+		app_notify_on_render_2d(UIBase.renderCursor);
+		///end
+		app_notify_on_update(UINodes.update);
+		app_notify_on_render_2d(UINodes.render);
+		app_notify_on_update(UIBase.update);
+		app_notify_on_render_2d(UIBase.render);
+		app_notify_on_update(Camera.update);
+		app_notify_on_render_2d(Base.render);
 
-					///if (is_paint || is_sculpt)
-					Base.appx = UIToolbar.toolbarw;
-					///end
-					///if is_lab
-					Base.appx = 0;
-					///end
+		///if (is_paint || is_sculpt)
+		Base.appx = UIToolbar.toolbarw;
+		///end
+		///if is_lab
+		Base.appx = 0;
+		///end
 
-					Base.appy = UIHeader.headerh;
-					if (Config.raw.layout[LayoutSize.LayoutHeader] == 1) Base.appy += UIHeader.headerh;
-					let cam = scene_camera;
-					cam.data.fov = Math.floor(cam.data.fov * 100) / 100;
-					camera_object_build_proj(cam);
+		Base.appy = UIHeader.headerh;
+		if (Config.raw.layout[LayoutSize.LayoutHeader] == 1) Base.appy += UIHeader.headerh;
+		let cam = scene_camera;
+		cam.data.fov = Math.floor(cam.data.fov * 100) / 100;
+		camera_object_build_proj(cam);
 
-					Args.run();
+		Args.run();
 
-					///if (krom_android || krom_ios)
-					let hasProjects = Config.raw.recent_projects.length > 0;
-					///else
-					let hasProjects = true;
-					///end
+		///if (krom_android || krom_ios)
+		let hasProjects = Config.raw.recent_projects.length > 0;
+		///else
+		let hasProjects = true;
+		///end
 
-					if (Config.raw.splash_screen && hasProjects) {
-						BoxProjects.show();
-					}
-				});
-			});
-		});
+		if (Config.raw.splash_screen && hasProjects) {
+			BoxProjects.show();
+		}
 	}
 
 	static saveAndQuitCallback = (save: bool) => {

+ 3 - 4
base/Sources/BoxExport.ts

@@ -442,10 +442,9 @@ class BoxExport {
 
 	static parsePreset = () => {
 		let file = "export_presets/" + BoxExport.files[BoxExport.hpreset.position] + ".json";
-		data_get_blob(file, (blob: ArrayBuffer) => {
-			BoxExport.preset = JSON.parse(sys_buffer_to_string(blob));
-			data_delete_blob("export_presets/" + file);
-		});
+		let blob: ArrayBuffer = data_get_blob(file);
+		BoxExport.preset = JSON.parse(sys_buffer_to_string(blob));
+		data_delete_blob("export_presets/" + file);
 	}
 
 	static newPreset = (name: string) => {

+ 12 - 15
base/Sources/BoxPreferences.ts

@@ -100,15 +100,14 @@ class BoxPreferences {
 						}
 						if (UIMenu.menuButton(ui, tr("Import..."))) {
 							UIFiles.show("json", false, false, (path: string) => {
-								data_get_blob(path, (b: ArrayBuffer) => {
-									let raw = JSON.parse(sys_buffer_to_string(b));
-									app_notify_on_init(() => {
-										ui.t.ELEMENT_H = Base.defaultElementH;
-										Config.importFrom(raw);
-										BoxPreferences.setScale();
-										MakeMaterial.parseMeshMaterial();
-										MakeMaterial.parsePaintMaterial();
-									});
+								let b: ArrayBuffer = data_get_blob(path);
+								let raw = JSON.parse(sys_buffer_to_string(b));
+								app_notify_on_init(() => {
+									ui.t.ELEMENT_H = Base.defaultElementH;
+									Config.importFrom(raw);
+									BoxPreferences.setScale();
+									MakeMaterial.parseMeshMaterial();
+									MakeMaterial.parsePaintMaterial();
 								});
 							});
 						}
@@ -620,12 +619,10 @@ plugin.drawUI = (ui) { =>
 								File.start(path);
 							}
 							if (UIMenu.menuButton(ui, tr("Edit in Script Tab"))) {
-								data_get_blob("plugins/" + f, (blob: ArrayBuffer) => {
-									TabScript.hscript.text = sys_buffer_to_string(blob);
-									data_delete_blob("plugins/" + f);
-									Console.info(tr("Script opened"));
-								});
-
+								let blob: ArrayBuffer = data_get_blob("plugins/" + f);
+								TabScript.hscript.text = sys_buffer_to_string(blob);
+								data_delete_blob("plugins/" + f);
+								Console.info(tr("Script opened"));
 							}
 							if (UIMenu.menuButton(ui, tr("Export"))) {
 								UIFiles.show("js", true, false, (dest: string) => {

+ 6 - 8
base/Sources/BoxProjects.ts

@@ -100,10 +100,9 @@ class BoxProjects {
 					if (BoxProjects.iconMap == null) BoxProjects.iconMap = new Map();
 					let icon = BoxProjects.iconMap.get(iconPath);
 					if (icon == null) {
-						data_get_image(iconPath, (image: image_t) => {
-							icon = image;
-							BoxProjects.iconMap.set(iconPath, icon);
-						});
+						let image: image_t = data_get_image(iconPath);
+						icon = image;
+						BoxProjects.iconMap.set(iconPath, icon);
 					}
 
 					let uix = ui._x;
@@ -218,10 +217,9 @@ class BoxProjects {
 	}
 
 	static drawBadge = (ui: zui_t) => {
-		data_get_image("badge.k", (img: image_t) => {
-			zui_image(img);
-			zui_end_element();
-		});
+		let img: image_t = data_get_image("badge.k");
+		zui_image(img);
+		zui_end_element();
 	}
 
 	static getStartedTab = (ui: zui_t) => {

+ 30 - 37
base/Sources/Config.ts

@@ -10,21 +10,18 @@ class Config {
 
 	static load = (done: ()=>void) => {
 		try {
-			data_get_blob((Path.isProtected() ? krom_save_path() : "") + "config.json", (blob: ArrayBuffer) => {
-				Config.configLoaded = true;
-				Config.raw = JSON.parse(sys_buffer_to_string(blob));
-
-				done();
-			});
+			let blob: ArrayBuffer = data_get_blob((Path.isProtected() ? krom_save_path() : "") + "config.json");
+			Config.configLoaded = true;
+			Config.raw = JSON.parse(sys_buffer_to_string(blob));
+			done();
 		}
 		catch (e: any) {
 			///if krom_linux
 			try { // Protected directory
-				data_get_blob(krom_save_path() + "config.json", (blob: ArrayBuffer) => {
-					Config.configLoaded = true;
-					Config.raw = JSON.parse(sys_buffer_to_string(blob));
-					done();
-				});
+				let blob: ArrayBuffer = data_get_blob(krom_save_path() + "config.json");
+				Config.configLoaded = true;
+				Config.raw = JSON.parse(sys_buffer_to_string(blob));
+				done();
 			}
 			catch (e: any) {
 				done();
@@ -110,17 +107,15 @@ class Config {
 
 	static getSha = (): string => {
 		let sha = "";
-		data_get_blob("version.json", (blob: ArrayBuffer) => {
-			sha = JSON.parse(sys_buffer_to_string(blob)).sha;
-		});
+		let blob: ArrayBuffer = data_get_blob("version.json");
+		sha = JSON.parse(sys_buffer_to_string(blob)).sha;
 		return sha;
 	}
 
 	static getDate = (): string => {
 		let date = "";
-		data_get_blob("version.json", (blob: ArrayBuffer) => {
-			date = JSON.parse(sys_buffer_to_string(blob)).date;
-		});
+		let blob: ArrayBuffer = data_get_blob("version.json");
+		date = JSON.parse(sys_buffer_to_string(blob)).date;
 		return date;
 	}
 
@@ -190,16 +185,15 @@ class Config {
 			Config.keymap = Base.defaultKeymap;
 		}
 		else {
-			data_get_blob("keymap_presets/" + Config.raw.keymap, (blob: ArrayBuffer) => {
-				Config.keymap = JSON.parse(sys_buffer_to_string(blob));
-				// Fill in undefined keys with defaults
-				for (let field in Base.defaultKeymap) {
-					if (!(field in Config.keymap)) {
-						let adefaultKeymap: any = Base.defaultKeymap;
-						Config.keymap[field] = adefaultKeymap[field];
-					}
+			let blob: ArrayBuffer = data_get_blob("keymap_presets/" + Config.raw.keymap);
+			Config.keymap = JSON.parse(sys_buffer_to_string(blob));
+			// Fill in undefined keys with defaults
+			for (let field in Base.defaultKeymap) {
+				if (!(field in Config.keymap)) {
+					let adefaultKeymap: any = Base.defaultKeymap;
+					Config.keymap[field] = adefaultKeymap[field];
 				}
-			});
+			}
 		}
 	}
 
@@ -274,17 +268,16 @@ class Config {
 			Base.theme = zui_theme_create();
 		}
 		else {
-			data_get_blob("themes/" + theme, (b: ArrayBuffer) => {
-				let parsed = JSON.parse(sys_buffer_to_string(b));
-				Base.theme = zui_theme_create();
-				for (let key in Base.theme) {
-					if (key == "theme_") continue;
-					if (key.startsWith("set_")) continue;
-					if (key.startsWith("get_")) key = key.substr(4);
-					let atheme: any = Base.theme;
-					atheme[key] = parsed[key];
-				}
-			});
+			let b: ArrayBuffer = data_get_blob("themes/" + theme);
+			let parsed = JSON.parse(sys_buffer_to_string(b));
+			Base.theme = zui_theme_create();
+			for (let key in Base.theme) {
+				if (key == "theme_") continue;
+				if (key.startsWith("set_")) continue;
+				if (key.startsWith("get_")) key = key.substr(4);
+				let atheme: any = Base.theme;
+				atheme[key] = parsed[key];
+			}
 		}
 		Base.theme.FILL_WINDOW_BG = true;
 		if (tagRedraw) {

+ 326 - 341
base/Sources/ImportArm.ts

@@ -2,399 +2,390 @@
 class ImportArm {
 
 	static runProject = (path: string) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			let project: TProjectFormat = armpack_decode(b);
-
-			///if (is_paint || is_sculpt)
-			if (project.version != null && project.layer_datas == null) {
-				// Import as material
-				if (project.material_nodes != null) {
-					ImportArm.runMaterialFromProject(project, path);
-				}
-				// Import as brush
-				else if (project.brush_nodes != null) {
-					ImportArm.runBrushFromProject(project, path);
-				}
-				// Import as swatches
-				else if (project.swatches != null) {
-					ImportArm.runSwatchesFromProject(project, path);
-				}
-				return;
+		let b: ArrayBuffer = data_get_blob(path);
+		let project: TProjectFormat = armpack_decode(b);
+
+		///if (is_paint || is_sculpt)
+		if (project.version != null && project.layer_datas == null) {
+			// Import as material
+			if (project.material_nodes != null) {
+				ImportArm.runMaterialFromProject(project, path);
 			}
+			// Import as brush
+			else if (project.brush_nodes != null) {
+				ImportArm.runBrushFromProject(project, path);
+			}
+			// Import as swatches
+			else if (project.swatches != null) {
+				ImportArm.runSwatchesFromProject(project, path);
+			}
+			return;
+		}
 
-			let importAsMesh = project.version == null;
-			Context.raw.layersPreviewDirty = true;
-			Context.raw.layerFilter = 0;
-			///end
+		let importAsMesh = project.version == null;
+		Context.raw.layersPreviewDirty = true;
+		Context.raw.layerFilter = 0;
+		///end
 
-			///if is_lab
-			let importAsMesh = true;
-			///end
+		///if is_lab
+		let importAsMesh = true;
+		///end
 
-			Project.projectNew(importAsMesh);
-			Project.filepath = path;
-			UIFiles.filename = path.substring(path.lastIndexOf(Path.sep) + 1, path.lastIndexOf("."));
-			///if (krom_android || krom_ios)
-			sys_title_set(UIFiles.filename);
-			///else
-			sys_title_set(UIFiles.filename + " - " + manifest_title);
-			///end
+		Project.projectNew(importAsMesh);
+		Project.filepath = path;
+		UIFiles.filename = path.substring(path.lastIndexOf(Path.sep) + 1, path.lastIndexOf("."));
+		///if (krom_android || krom_ios)
+		sys_title_set(UIFiles.filename);
+		///else
+		sys_title_set(UIFiles.filename + " - " + manifest_title);
+		///end
 
-			///if (is_paint || is_sculpt)
-			// Import as mesh instead
-			if (importAsMesh) {
-				ImportArm.runMesh(project);
-				return;
-			}
-			///end
+		///if (is_paint || is_sculpt)
+		// Import as mesh instead
+		if (importAsMesh) {
+			ImportArm.runMesh(project);
+			return;
+		}
+		///end
+
+		// Save to recent
+		///if krom_ios
+		let recent_path = path.substr(path.lastIndexOf("/") + 1);
+		///else
+		let recent_path = path;
+		///end
+		let recent = Config.raw.recent_projects;
+		array_remove(recent, recent_path);
+		recent.unshift(recent_path);
+		Config.save();
+
+		Project.raw = project;
+
+		///if (is_paint || is_sculpt)
+		let l0 = project.layer_datas[0];
+		Base.resHandle.position = Config.getTextureResPos(l0.res);
+		let bitsPos = l0.bpp == 8 ? TextureBits.Bits8 : l0.bpp == 16 ? TextureBits.Bits16 : TextureBits.Bits32;
+		Base.bitsHandle.position = bitsPos;
+		let bytesPerPixel = Math.floor(l0.bpp / 8);
+		let format = l0.bpp == 8 ? tex_format_t.RGBA32 : l0.bpp == 16 ? tex_format_t.RGBA64 : tex_format_t.RGBA128;
+		///end
 
-			// Save to recent
-			///if krom_ios
-			let recent_path = path.substr(path.lastIndexOf("/") + 1);
+		let base = Path.baseDir(path);
+		if (Project.raw.envmap != null) {
+			Project.raw.envmap = data_is_abs(Project.raw.envmap) ? Project.raw.envmap : base + Project.raw.envmap;
+		}
+		if (Project.raw.envmap_strength != null) {
+			scene_world.strength = Project.raw.envmap_strength;
+		}
+		if (Project.raw.camera_world != null) {
+			scene_camera.base.transform.local = mat4_from_f32_array(Project.raw.camera_world);
+			transform_decompose(scene_camera.base.transform);
+			scene_camera.data.fov = Project.raw.camera_fov;
+			camera_object_build_proj(scene_camera);
+			let origin = Project.raw.camera_origin;
+			Camera.origins[0].x = origin[0];
+			Camera.origins[0].y = origin[1];
+			Camera.origins[0].z = origin[2];
+		}
+
+		for (let file of project.assets) {
+			///if krom_windows
+			file = file.replaceAll("/", "\\");
 			///else
-			let recent_path = path;
-			///end
-			let recent = Config.raw.recent_projects;
-			array_remove(recent, recent_path);
-			recent.unshift(recent_path);
-			Config.save();
-
-			Project.raw = project;
-
-			///if (is_paint || is_sculpt)
-			let l0 = project.layer_datas[0];
-			Base.resHandle.position = Config.getTextureResPos(l0.res);
-			let bitsPos = l0.bpp == 8 ? TextureBits.Bits8 : l0.bpp == 16 ? TextureBits.Bits16 : TextureBits.Bits32;
-			Base.bitsHandle.position = bitsPos;
-			let bytesPerPixel = Math.floor(l0.bpp / 8);
-			let format = l0.bpp == 8 ? tex_format_t.RGBA32 : l0.bpp == 16 ? tex_format_t.RGBA64 : tex_format_t.RGBA128;
+			file = file.replaceAll("\\", "/");
 			///end
-
-			let base = Path.baseDir(path);
-			if (Project.raw.envmap != null) {
-				Project.raw.envmap = data_is_abs(Project.raw.envmap) ? Project.raw.envmap : base + Project.raw.envmap;
-			}
-			if (Project.raw.envmap_strength != null) {
-				scene_world.strength = Project.raw.envmap_strength;
+			// Convert image path from relative to absolute
+			let abs = data_is_abs(file) ? file : base + file;
+			if (project.packed_assets != null) {
+				abs = Path.normalize(abs);
+				ImportArm.unpackAsset(project, abs, file);
 			}
-			if (Project.raw.camera_world != null) {
-				scene_camera.base.transform.local = mat4_from_f32_array(Project.raw.camera_world);
-				transform_decompose(scene_camera.base.transform);
-				scene_camera.data.fov = Project.raw.camera_fov;
-				camera_object_build_proj(scene_camera);
-				let origin = Project.raw.camera_origin;
-				Camera.origins[0].x = origin[0];
-				Camera.origins[0].y = origin[1];
-				Camera.origins[0].z = origin[2];
+			if (data_cached_images.get(abs) == null && !File.exists(abs)) {
+				ImportArm.makePink(abs);
 			}
+			let hdrAsEnvmap = abs.endsWith(".hdr") && Project.raw.envmap == abs;
+			ImportTexture.run(abs, hdrAsEnvmap);
+		}
 
-			for (let file of project.assets) {
+		///if (is_paint || is_sculpt)
+		if (project.font_assets != null) {
+			for (let file of project.font_assets) {
 				///if krom_windows
 				file = file.replaceAll("/", "\\");
 				///else
 				file = file.replaceAll("\\", "/");
 				///end
-				// Convert image path from relative to absolute
+				// Convert font path from relative to absolute
 				let abs = data_is_abs(file) ? file : base + file;
-				if (project.packed_assets != null) {
-					abs = Path.normalize(abs);
-					ImportArm.unpackAsset(project, abs, file);
-				}
-				if (data_cached_images.get(abs) == null && !File.exists(abs)) {
-					ImportArm.makePink(abs);
+				if (File.exists(abs)) {
+					ImportFont.run(abs);
 				}
-				let hdrAsEnvmap = abs.endsWith(".hdr") && Project.raw.envmap == abs;
-				ImportTexture.run(abs, hdrAsEnvmap);
 			}
+		}
+		///end
 
-			///if (is_paint || is_sculpt)
-			if (project.font_assets != null) {
-				for (let file of project.font_assets) {
-					///if krom_windows
-					file = file.replaceAll("/", "\\");
-					///else
-					file = file.replaceAll("\\", "/");
-					///end
-					// Convert font path from relative to absolute
-					let abs = data_is_abs(file) ? file : base + file;
-					if (File.exists(abs)) {
-						ImportFont.run(abs);
-					}
-				}
-			}
-			///end
+		///if (is_paint || is_sculpt)
+		let md: mesh_data_t = mesh_data_create(project.mesh_datas[0]);
+		///end
 
-			// Synchronous for now
-			///if (is_paint || is_sculpt)
-			mesh_data_create(project.mesh_datas[0], (md: mesh_data_t) => {
-			///end
+		///if is_lab
+		let md: mesh_data_t = mesh_data_create(project.mesh_data);
+		///end
 
-			///if is_lab
-			mesh_data_create(project.mesh_data, (md: mesh_data_t) => {
-			///end
+		mesh_object_set_data(Context.raw.paintObject, md);
+		vec4_set(Context.raw.paintObject.base.transform.scale, 1, 1, 1);
+		transform_build_matrix(Context.raw.paintObject.base.transform);
+		Context.raw.paintObject.base.name = md.name;
+		Project.paintObjects = [Context.raw.paintObject];
 
-				mesh_object_set_data(Context.raw.paintObject, md);
-				vec4_set(Context.raw.paintObject.base.transform.scale, 1, 1, 1);
-				transform_build_matrix(Context.raw.paintObject.base.transform);
-				Context.raw.paintObject.base.name = md.name;
-				Project.paintObjects = [Context.raw.paintObject];
-			});
+		///if (is_paint || is_sculpt)
+		for (let i = 1; i < project.mesh_datas.length; ++i) {
+			let raw = project.mesh_datas[i];
+			let md: mesh_data_t = mesh_data_create(raw);
+			let object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
+			object.base.name = md.name;
+			object.skip_context = "paint";
+			Project.paintObjects.push(object);
+		}
 
-			///if (is_paint || is_sculpt)
-			for (let i = 1; i < project.mesh_datas.length; ++i) {
-				let raw = project.mesh_datas[i];
-				mesh_data_create(raw, (md: mesh_data_t) => {
-					let object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
-					object.base.name = md.name;
-					object.skip_context = "paint";
-					Project.paintObjects.push(object);
-				});
-			}
+		if (project.mesh_assets != null && project.mesh_assets.length > 0) {
+			let file = project.mesh_assets[0];
+			let abs = data_is_abs(file) ? file : base + file;
+			Project.meshAssets = [abs];
+		}
 
-			if (project.mesh_assets != null && project.mesh_assets.length > 0) {
-				let file = project.mesh_assets[0];
-				let abs = data_is_abs(file) ? file : base + file;
-				Project.meshAssets = [abs];
-			}
+		///if is_paint
+		if (project.atlas_objects != null) Project.atlasObjects = project.atlas_objects;
+		if (project.atlas_names != null) Project.atlasNames = project.atlas_names;
+		///end
+
+		// No mask by default
+		if (Context.raw.mergedObject == null) UtilMesh.mergeMesh();
+		///end
+
+		Context.selectPaintObject(Context.mainObject());
+		Viewport.scaleToBounds();
+		Context.raw.paintObject.skip_context = "paint";
+		Context.raw.mergedObject.base.visible = true;
+
+		///if (is_paint || is_sculpt)
+		let tex = Project.layers[0].texpaint;
+		if (tex.width != Config.getTextureResX() || tex.height != Config.getTextureResY()) {
+			if (History.undoLayers != null) for (let l of History.undoLayers) SlotLayer.resizeAndSetBits(l);
+			let rts = render_path_render_targets;
+			let _texpaint_blend0 = rts.get("texpaint_blend0").image;
+			Base.notifyOnNextFrame(() => {
+				image_unload(_texpaint_blend0);
+			});
+			rts.get("texpaint_blend0").width = Config.getTextureResX();
+			rts.get("texpaint_blend0").height = Config.getTextureResY();
+			rts.get("texpaint_blend0").image = image_create_render_target(Config.getTextureResX(), Config.getTextureResY(), tex_format_t.R8, depth_format_t.NO_DEPTH);
+			let _texpaint_blend1 = rts.get("texpaint_blend1").image;
+			Base.notifyOnNextFrame(() => {
+				image_unload(_texpaint_blend1);
+			});
+			rts.get("texpaint_blend1").width = Config.getTextureResX();
+			rts.get("texpaint_blend1").height = Config.getTextureResY();
+			rts.get("texpaint_blend1").image = image_create_render_target(Config.getTextureResX(), Config.getTextureResY(), tex_format_t.R8, depth_format_t.NO_DEPTH);
+			Context.raw.brushBlendDirty = true;
+		}
+
+		for (let l of Project.layers) SlotLayer.unload(l);
+		Project.layers = [];
+		for (let i = 0; i < project.layer_datas.length; ++i) {
+			let ld = project.layer_datas[i];
+			let isGroup = ld.texpaint == null;
 
 			///if is_paint
-			if (project.atlas_objects != null) Project.atlasObjects = project.atlas_objects;
-			if (project.atlas_names != null) Project.atlasNames = project.atlas_names;
+			let isMask = ld.texpaint != null && ld.texpaint_nor == null;
 			///end
-
-			// No mask by default
-			if (Context.raw.mergedObject == null) UtilMesh.mergeMesh();
+			///if is_sculpt
+			let isMask = false;
 			///end
 
-			Context.selectPaintObject(Context.mainObject());
-			Viewport.scaleToBounds();
-			Context.raw.paintObject.skip_context = "paint";
-			Context.raw.mergedObject.base.visible = true;
-
-			///if (is_paint || is_sculpt)
-			let tex = Project.layers[0].texpaint;
-			if (tex.width != Config.getTextureResX() || tex.height != Config.getTextureResY()) {
-				if (History.undoLayers != null) for (let l of History.undoLayers) SlotLayer.resizeAndSetBits(l);
-				let rts = render_path_render_targets;
-				let _texpaint_blend0 = rts.get("texpaint_blend0").image;
-				Base.notifyOnNextFrame(() => {
-					image_unload(_texpaint_blend0);
-				});
-				rts.get("texpaint_blend0").width = Config.getTextureResX();
-				rts.get("texpaint_blend0").height = Config.getTextureResY();
-				rts.get("texpaint_blend0").image = image_create_render_target(Config.getTextureResX(), Config.getTextureResY(), tex_format_t.R8, depth_format_t.NO_DEPTH);
-				let _texpaint_blend1 = rts.get("texpaint_blend1").image;
-				Base.notifyOnNextFrame(() => {
-					image_unload(_texpaint_blend1);
-				});
-				rts.get("texpaint_blend1").width = Config.getTextureResX();
-				rts.get("texpaint_blend1").height = Config.getTextureResY();
-				rts.get("texpaint_blend1").image = image_create_render_target(Config.getTextureResX(), Config.getTextureResY(), tex_format_t.R8, depth_format_t.NO_DEPTH);
-				Context.raw.brushBlendDirty = true;
-			}
+			let l = SlotLayer.create("", isGroup ? LayerSlotType.SlotGroup : isMask ? LayerSlotType.SlotMask : LayerSlotType.SlotLayer);
+			if (ld.name != null) l.name = ld.name;
+			l.visible = ld.visible;
+			Project.layers.push(l);
+
+			if (!isGroup) {
+				if (Base.pipeMerge == null) Base.makePipe();
 
-			for (let l of Project.layers) SlotLayer.unload(l);
-			Project.layers = [];
-			for (let i = 0; i < project.layer_datas.length; ++i) {
-				let ld = project.layer_datas[i];
-				let isGroup = ld.texpaint == null;
+				let _texpaint: image_t = null;
 
 				///if is_paint
-				let isMask = ld.texpaint != null && ld.texpaint_nor == null;
+				let _texpaint_nor: image_t = null;
+				let _texpaint_pack: image_t = null;
 				///end
-				///if is_sculpt
-				let isMask = false;
-				///end
-
-				let l = SlotLayer.create("", isGroup ? LayerSlotType.SlotGroup : isMask ? LayerSlotType.SlotMask : LayerSlotType.SlotLayer);
-				if (ld.name != null) l.name = ld.name;
-				l.visible = ld.visible;
-				Project.layers.push(l);
-
-				if (!isGroup) {
-					if (Base.pipeMerge == null) Base.makePipe();
 
-					let _texpaint: image_t = null;
+				if (isMask) {
+					_texpaint = image_from_bytes(lz4_decode(ld.texpaint, ld.res * ld.res * 4), ld.res, ld.res, tex_format_t.RGBA32);
+					g2_begin(l.texpaint, false);
+					// g2_set_pipeline(Base.pipeCopy8);
+					g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy); // Full bits for undo support, R8 is used
+					g2_draw_image(_texpaint, 0, 0);
+					g2_set_pipeline(null);
+					g2_end();
+				}
+				else { // Layer
+					// TODO: create render target from bytes
+					_texpaint = image_from_bytes(lz4_decode(ld.texpaint, ld.res * ld.res * 4 * bytesPerPixel), ld.res, ld.res, format);
+					g2_begin(l.texpaint, false);
+					g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy);
+					g2_draw_image(_texpaint, 0, 0);
+					g2_set_pipeline(null);
+					g2_end();
 
 					///if is_paint
-					let _texpaint_nor: image_t = null;
-					let _texpaint_pack: image_t = null;
+					_texpaint_nor = image_from_bytes(lz4_decode(ld.texpaint_nor, ld.res * ld.res * 4 * bytesPerPixel), ld.res, ld.res, format);
+					g2_begin(l.texpaint_nor, false);
+					g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy);
+					g2_draw_image(_texpaint_nor, 0, 0);
+					g2_set_pipeline(null);
+					g2_end();
+
+					_texpaint_pack = image_from_bytes(lz4_decode(ld.texpaint_pack, ld.res * ld.res * 4 * bytesPerPixel), ld.res, ld.res, format);
+					g2_begin(l.texpaint_pack, false);
+					g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy);
+					g2_draw_image(_texpaint_pack, 0, 0);
+					g2_set_pipeline(null);
+					g2_end();
 					///end
+				}
 
-					if (isMask) {
-						_texpaint = image_from_bytes(lz4_decode(ld.texpaint, ld.res * ld.res * 4), ld.res, ld.res, tex_format_t.RGBA32);
-						g2_begin(l.texpaint, false);
-						// g2_set_pipeline(Base.pipeCopy8);
-						g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy); // Full bits for undo support, R8 is used
-						g2_draw_image(_texpaint, 0, 0);
-						g2_set_pipeline(null);
-						g2_end();
-					}
-					else { // Layer
-						// TODO: create render target from bytes
-						_texpaint = image_from_bytes(lz4_decode(ld.texpaint, ld.res * ld.res * 4 * bytesPerPixel), ld.res, ld.res, format);
-						g2_begin(l.texpaint, false);
-						g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy);
-						g2_draw_image(_texpaint, 0, 0);
-						g2_set_pipeline(null);
-						g2_end();
-
-						///if is_paint
-						_texpaint_nor = image_from_bytes(lz4_decode(ld.texpaint_nor, ld.res * ld.res * 4 * bytesPerPixel), ld.res, ld.res, format);
-						g2_begin(l.texpaint_nor, false);
-						g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy);
-						g2_draw_image(_texpaint_nor, 0, 0);
-						g2_set_pipeline(null);
-						g2_end();
-
-						_texpaint_pack = image_from_bytes(lz4_decode(ld.texpaint_pack, ld.res * ld.res * 4 * bytesPerPixel), ld.res, ld.res, format);
-						g2_begin(l.texpaint_pack, false);
-						g2_set_pipeline(project.is_bgra ? Base.pipeCopyBGRA : Base.pipeCopy);
-						g2_draw_image(_texpaint_pack, 0, 0);
-						g2_set_pipeline(null);
-						g2_end();
-						///end
-					}
-
-					l.scale = ld.uv_scale;
-					l.angle = ld.uv_rot;
-					l.uvType = ld.uv_type;
-					if (ld.decal_mat != null) l.decalMat = mat4_from_f32_array(ld.decal_mat);
-					l.maskOpacity = ld.opacity_mask;
-					l.objectMask = ld.object_mask;
-					l.blending = ld.blending;
+				l.scale = ld.uv_scale;
+				l.angle = ld.uv_rot;
+				l.uvType = ld.uv_type;
+				if (ld.decal_mat != null) l.decalMat = mat4_from_f32_array(ld.decal_mat);
+				l.maskOpacity = ld.opacity_mask;
+				l.objectMask = ld.object_mask;
+				l.blending = ld.blending;
 
+				///if is_paint
+				l.paintBase = ld.paint_base;
+				l.paintOpac = ld.paint_opac;
+				l.paintOcc = ld.paint_occ;
+				l.paintRough = ld.paint_rough;
+				l.paintMet = ld.paint_met;
+				l.paintNor = ld.paint_nor;
+				l.paintNorBlend = ld.paint_nor_blend != null ? ld.paint_nor_blend : true; // TODO: deprecated
+				l.paintHeight = ld.paint_height;
+				l.paintHeightBlend = ld.paint_height_blend != null ? ld.paint_height_blend : true; // TODO: deprecated
+				l.paintEmis = ld.paint_emis;
+				l.paintSubs = ld.paint_subs;
+				///end
+
+				Base.notifyOnNextFrame(() => {
+					image_unload(_texpaint);
 					///if is_paint
-					l.paintBase = ld.paint_base;
-					l.paintOpac = ld.paint_opac;
-					l.paintOcc = ld.paint_occ;
-					l.paintRough = ld.paint_rough;
-					l.paintMet = ld.paint_met;
-					l.paintNor = ld.paint_nor;
-					l.paintNorBlend = ld.paint_nor_blend != null ? ld.paint_nor_blend : true; // TODO: deprecated
-					l.paintHeight = ld.paint_height;
-					l.paintHeightBlend = ld.paint_height_blend != null ? ld.paint_height_blend : true; // TODO: deprecated
-					l.paintEmis = ld.paint_emis;
-					l.paintSubs = ld.paint_subs;
+					if (_texpaint_nor != null) image_unload(_texpaint_nor);
+					if (_texpaint_pack != null) image_unload(_texpaint_pack);
 					///end
-
-					Base.notifyOnNextFrame(() => {
-						image_unload(_texpaint);
-						///if is_paint
-						if (_texpaint_nor != null) image_unload(_texpaint_nor);
-						if (_texpaint_pack != null) image_unload(_texpaint_pack);
-						///end
-					});
-				}
+				});
 			}
+		}
 
-			// Assign parents to groups and masks
-			for (let i = 0; i < project.layer_datas.length; ++i) {
-				let ld = project.layer_datas[i];
-				if (ld.parent >= 0) {
-					Project.layers[i].parent = Project.layers[ld.parent];
-				}
+		// Assign parents to groups and masks
+		for (let i = 0; i < project.layer_datas.length; ++i) {
+			let ld = project.layer_datas[i];
+			if (ld.parent >= 0) {
+				Project.layers[i].parent = Project.layers[ld.parent];
 			}
+		}
 
-			Context.setLayer(Project.layers[0]);
+		Context.setLayer(Project.layers[0]);
 
-			// Materials
-			let m0: material_data_t = null;
-			data_get_material("Scene", "Material", (m: material_data_t) => {
-				m0 = m;
-			});
+		// Materials
+		let m0: material_data_t = data_get_material("Scene", "Material");
 
-			Project.materials = [];
-			for (let n of project.material_nodes) {
-				ImportArm.initNodes(n.nodes);
-				Context.raw.material = SlotMaterial.create(m0, n);
-				Project.materials.push(Context.raw.material);
-			}
-			///end
+		Project.materials = [];
+		for (let n of project.material_nodes) {
+			ImportArm.initNodes(n.nodes);
+			Context.raw.material = SlotMaterial.create(m0, n);
+			Project.materials.push(Context.raw.material);
+		}
+		///end
 
-			UINodes.hwnd.redraws = 2;
-			UINodes.groupStack = [];
-			Project.materialGroups = [];
-			if (project.material_groups != null) {
-				for (let g of project.material_groups) Project.materialGroups.push({ canvas: g, nodes: zui_nodes_create() });
-			}
+		UINodes.hwnd.redraws = 2;
+		UINodes.groupStack = [];
+		Project.materialGroups = [];
+		if (project.material_groups != null) {
+			for (let g of project.material_groups) Project.materialGroups.push({ canvas: g, nodes: zui_nodes_create() });
+		}
 
-			///if (is_paint || is_sculpt)
-			for (let m of Project.materials) {
-				Context.raw.material = m;
-				MakeMaterial.parsePaintMaterial();
-				UtilRender.makeMaterialPreview();
-			}
+		///if (is_paint || is_sculpt)
+		for (let m of Project.materials) {
+			Context.raw.material = m;
+			MakeMaterial.parsePaintMaterial();
+			UtilRender.makeMaterialPreview();
+		}
 
-			Project.brushes = [];
-			for (let n of project.brush_nodes) {
-				ImportArm.initNodes(n.nodes);
-				Context.raw.brush = SlotBrush.create(n);
-				Project.brushes.push(Context.raw.brush);
-				MakeMaterial.parseBrush();
-				UtilRender.makeBrushPreview();
-			}
+		Project.brushes = [];
+		for (let n of project.brush_nodes) {
+			ImportArm.initNodes(n.nodes);
+			Context.raw.brush = SlotBrush.create(n);
+			Project.brushes.push(Context.raw.brush);
+			MakeMaterial.parseBrush();
+			UtilRender.makeBrushPreview();
+		}
 
-			// Fill layers
-			for (let i = 0; i < project.layer_datas.length; ++i) {
-				let ld = project.layer_datas[i];
-				let l = Project.layers[i];
-				let isGroup = ld.texpaint == null;
-				if (!isGroup) {
-					l.fill_layer = ld.fill_layer > -1 ? Project.materials[ld.fill_layer] : null;
-				}
+		// Fill layers
+		for (let i = 0; i < project.layer_datas.length; ++i) {
+			let ld = project.layer_datas[i];
+			let l = Project.layers[i];
+			let isGroup = ld.texpaint == null;
+			if (!isGroup) {
+				l.fill_layer = ld.fill_layer > -1 ? Project.materials[ld.fill_layer] : null;
 			}
+		}
 
-			UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
-			UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
-			///end
+		UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
+		UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
+		///end
 
-			///if is_lab
-			ImportArm.initNodes(project.material.nodes);
-			Project.canvas = project.material;
-			ParserLogic.parse(Project.canvas);
-			///end
+		///if is_lab
+		ImportArm.initNodes(project.material.nodes);
+		Project.canvas = project.material;
+		ParserLogic.parse(Project.canvas);
+		///end
 
-			Context.raw.ddirty = 4;
-			data_delete_blob(path);
-		});
+		Context.raw.ddirty = 4;
+		data_delete_blob(path);
 	}
 
 	///if (is_paint || is_sculpt)
 	static runMesh = (raw: scene_t) => {
 		Project.paintObjects = [];
 		for (let i = 0; i < raw.mesh_datas.length; ++i) {
-			mesh_data_create(raw.mesh_datas[i], (md: mesh_data_t) => {
-				let object: mesh_object_t = null;
-				if (i == 0) {
-					mesh_object_set_data(Context.raw.paintObject, md);
-					object = Context.raw.paintObject;
-				}
-				else {
-					object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
-					object.base.name = md.name;
-					object.skip_context = "paint";
-					md._handle = md.name;
-					data_cached_meshes.set(md._handle, md);
-				}
-				vec4_set(object.base.transform.scale, 1, 1, 1);
-				transform_build_matrix(object.base.transform);
+			let md: mesh_data_t = mesh_data_create(raw.mesh_datas[i]);
+			let object: mesh_object_t = null;
+			if (i == 0) {
+				mesh_object_set_data(Context.raw.paintObject, md);
+				object = Context.raw.paintObject;
+			}
+			else {
+				object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
 				object.base.name = md.name;
-				Project.paintObjects.push(object);
-				UtilMesh.mergeMesh();
-				Viewport.scaleToBounds();
-			});
+				object.skip_context = "paint";
+				md._handle = md.name;
+				data_cached_meshes.set(md._handle, md);
+			}
+			vec4_set(object.base.transform.scale, 1, 1, 1);
+			transform_build_matrix(object.base.transform);
+			object.base.name = md.name;
+			Project.paintObjects.push(object);
+			UtilMesh.mergeMesh();
+			Viewport.scaleToBounds();
 		}
 		app_notify_on_init(Base.initLayers);
 		History.reset();
 	}
 
 	static runMaterial = (path: string) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			let project: TProjectFormat = armpack_decode(b);
-			if (project.version == null) { data_delete_blob(path); return; }
-			ImportArm.runMaterialFromProject(project, path);
-		});
+		let b: ArrayBuffer = data_get_blob(path);
+		let project: TProjectFormat = armpack_decode(b);
+		if (project.version == null) { data_delete_blob(path); return; }
+		ImportArm.runMaterialFromProject(project, path);
 	}
 
 	static runMaterialFromProject = (project: TProjectFormat, path: string) => {
@@ -417,10 +408,7 @@ class ImportArm {
 			ImportTexture.run(abs);
 		}
 
-		let m0: material_data_t = null;
-		data_get_material("Scene", "Material", (m: material_data_t) => {
-			m0 = m;
-		});
+		let m0: material_data_t = data_get_material("Scene", "Material");
 
 		let imported: SlotMaterialRaw[] = [];
 
@@ -476,11 +464,10 @@ class ImportArm {
 	}
 
 	static runBrush = (path: string) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			let project: TProjectFormat = armpack_decode(b);
-			if (project.version == null) { data_delete_blob(path); return; }
-			ImportArm.runBrushFromProject(project, path);
-		});
+		let b: ArrayBuffer = data_get_blob(path);
+		let project: TProjectFormat = armpack_decode(b);
+		if (project.version == null) { data_delete_blob(path); return; }
+		ImportArm.runBrushFromProject(project, path);
 	}
 
 	static runBrushFromProject = (project: TProjectFormat, path: string) => {
@@ -526,11 +513,10 @@ class ImportArm {
 	///end
 
 	static runSwatches = (path: string, replaceExisting = false) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			let project: TProjectFormat = armpack_decode(b);
-			if (project.version == null) { data_delete_blob(path); return; }
-			ImportArm.runSwatchesFromProject(project, path, replaceExisting);
-		});
+		let b: ArrayBuffer = data_get_blob(path);
+		let project: TProjectFormat = armpack_decode(b);
+		if (project.version == null) { data_delete_blob(path); return; }
+		ImportArm.runSwatchesFromProject(project, path, replaceExisting);
 	}
 
 	static runSwatchesFromProject = (project: TProjectFormat, path: string, replaceExisting = false) => {
@@ -595,9 +581,8 @@ class ImportArm {
 				if (!Project.packedAssetExists(Project.raw.packed_assets, pa.name)) {
 					Project.raw.packed_assets.push(pa);
 				}
-				image_from_encoded_bytes(pa.bytes, pa.name.endsWith(".jpg") ? ".jpg" : ".png", (image: image_t) => {
-					data_cached_images.set(abs, image);
-				}, false);
+				let image: image_t = image_from_encoded_bytes(pa.bytes, pa.name.endsWith(".jpg") ? ".jpg" : ".png");
+				data_cached_images.set(abs, image);
 				break;
 			}
 		}

+ 214 - 215
base/Sources/ImportBlendMaterial.ts

@@ -4,257 +4,256 @@
 class ImportBlendMaterial {
 
 	static run = (path: string) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			let bl = ParserBlend.init(b);
-			if (bl.dna == null) {
-				Console.error(Strings.error3());
-				return;
-			}
+		let b: ArrayBuffer = data_get_blob(path);
+		let bl = ParserBlend.init(b);
+		if (bl.dna == null) {
+			Console.error(Strings.error3());
+			return;
+		}
 
-			let mats = ParserBlend.get(bl, "Material");
-			if (mats.length == 0) {
-				Console.error("Error: No materials found");
-				return;
-			}
+		let mats = ParserBlend.get(bl, "Material");
+		if (mats.length == 0) {
+			Console.error("Error: No materials found");
+			return;
+		}
 
-			let imported: SlotMaterialRaw[] = [];
+		let imported: SlotMaterialRaw[] = [];
 
-			for (let mat of mats) {
-				// Material slot
-				Context.raw.material = SlotMaterial.create(Project.materials[0].data);
-				Project.materials.push(Context.raw.material);
-				imported.push(Context.raw.material);
-				let nodes = Context.raw.material.nodes;
-				let canvas = Context.raw.material.canvas;
-				canvas.name = BlHandle.get(BlHandle.get(mat, "id"), "name").substr(2); // MAWood
-				let nout: zui_node_t = null;
-				for (let n of canvas.nodes) {
-					if (n.type == "OUTPUT_MATERIAL_PBR") {
-						nout = n;
-						break;
-					}
+		for (let mat of mats) {
+			// Material slot
+			Context.raw.material = SlotMaterial.create(Project.materials[0].data);
+			Project.materials.push(Context.raw.material);
+			imported.push(Context.raw.material);
+			let nodes = Context.raw.material.nodes;
+			let canvas = Context.raw.material.canvas;
+			canvas.name = BlHandle.get(BlHandle.get(mat, "id"), "name").substr(2); // MAWood
+			let nout: zui_node_t = null;
+			for (let n of canvas.nodes) {
+				if (n.type == "OUTPUT_MATERIAL_PBR") {
+					nout = n;
+					break;
 				}
-				for (let n of canvas.nodes) {
-					if (n.name == "RGB") {
-						zui_remove_node(n, canvas);
-						break;
-					}
+			}
+			for (let n of canvas.nodes) {
+				if (n.name == "RGB") {
+					zui_remove_node(n, canvas);
+					break;
 				}
+			}
 
-				// Parse nodetree
-				let nodetree = BlHandle.get(mat, "nodetree"); // bNodeTree
-				let blnodes = BlHandle.get(nodetree, "nodes"); // ListBase
-				let bllinks = BlHandle.get(nodetree, "links"); // bNodeLink
+			// Parse nodetree
+			let nodetree = BlHandle.get(mat, "nodetree"); // bNodeTree
+			let blnodes = BlHandle.get(nodetree, "nodes"); // ListBase
+			let bllinks = BlHandle.get(nodetree, "links"); // bNodeLink
 
-				// Look for Principled BSDF node
-				let node: any = BlHandle.get(blnodes, "first", 0, "bNode");
-				let last = BlHandle.get(blnodes, "last", 0, "bNode");
-				while (true) {
-					if (BlHandle.get(node, "idname") == "ShaderNodeBsdfPrincipled") break;
-					if (BlHandle.get(node, "name") == BlHandle.get(last, "name")) break;
-					node = BlHandle.get(node, "next");
-				}
-				if (BlHandle.get(node, "idname") != "ShaderNodeBsdfPrincipled") {
-					Console.error("Error: No Principled BSDF node found");
-					continue;
-				}
+			// Look for Principled BSDF node
+			let node: any = BlHandle.get(blnodes, "first", 0, "bNode");
+			let last = BlHandle.get(blnodes, "last", 0, "bNode");
+			while (true) {
+				if (BlHandle.get(node, "idname") == "ShaderNodeBsdfPrincipled") break;
+				if (BlHandle.get(node, "name") == BlHandle.get(last, "name")) break;
+				node = BlHandle.get(node, "next");
+			}
+			if (BlHandle.get(node, "idname") != "ShaderNodeBsdfPrincipled") {
+				Console.error("Error: No Principled BSDF node found");
+				continue;
+			}
 
-				// Use Principled BSDF as material output
-				nout.name = BlHandle.get(node, "name");
-				nout.x = BlHandle.get(node, "locx") + 400;
-				nout.y = -BlHandle.get(node, "locy") + 400;
+			// Use Principled BSDF as material output
+			nout.name = BlHandle.get(node, "name");
+			nout.x = BlHandle.get(node, "locx") + 400;
+			nout.y = -BlHandle.get(node, "locy") + 400;
 
-				// Place nodes
-				node = BlHandle.get(blnodes, "first", 0, "bNode");
-				while (true) {
-					// Search for node in list
-					let search = BlHandle.get(node, "idname").substr(10).toLowerCase();
-					let base: zui_node_t = null;
-					for (let list of NodesMaterial.list) {
-						let found = false;
-						for (let n of list) {
-							let s = n.type.replaceAll("_", "").toLowerCase();
-							if (search == s) {
-								base = n;
-								found = true;
-								break;
-							}
+			// Place nodes
+			node = BlHandle.get(blnodes, "first", 0, "bNode");
+			while (true) {
+				// Search for node in list
+				let search = BlHandle.get(node, "idname").substr(10).toLowerCase();
+				let base: zui_node_t = null;
+				for (let list of NodesMaterial.list) {
+					let found = false;
+					for (let n of list) {
+						let s = n.type.replaceAll("_", "").toLowerCase();
+						if (search == s) {
+							base = n;
+							found = true;
+							break;
 						}
-						if (found) break;
 					}
+					if (found) break;
+				}
 
-					if (base != null) {
-						let n = UINodes.makeNode(base, nodes, canvas);
-						n.x = BlHandle.get(node, "locx") + 400;
-						n.y = -BlHandle.get(node, "locy") + 400;
-						n.name = BlHandle.get(node, "name");
+				if (base != null) {
+					let n = UINodes.makeNode(base, nodes, canvas);
+					n.x = BlHandle.get(node, "locx") + 400;
+					n.y = -BlHandle.get(node, "locy") + 400;
+					n.name = BlHandle.get(node, "name");
 
-						// Fill input socket values
-						let inputs = BlHandle.get(node, "inputs");
-						let sock: any = BlHandle.get(inputs, "first", 0, "bNodeSocket");
-						let pos = 0;
-						while (true) {
-							if (pos >= n.inputs.length) break;
-							n.inputs[pos].default_value = ImportBlendMaterial.readBlendSocket(sock);
+					// Fill input socket values
+					let inputs = BlHandle.get(node, "inputs");
+					let sock: any = BlHandle.get(inputs, "first", 0, "bNodeSocket");
+					let pos = 0;
+					while (true) {
+						if (pos >= n.inputs.length) break;
+						n.inputs[pos].default_value = ImportBlendMaterial.readBlendSocket(sock);
 
-							let last = sock;
-							sock = BlHandle.get(sock, "next");
-							if (last.block == sock.block) break;
-							pos++;
-						}
+						let last = sock;
+						sock = BlHandle.get(sock, "next");
+						if (last.block == sock.block) break;
+						pos++;
+					}
 
-						// Fill button values
-						if (search == "teximage") {
-							let img = BlHandle.get(node, "id", 0, "Image");
-							let file: string = BlHandle.get(img, "name").substr(2); // '//desktop\logo.png'
-							file = Path.baseDir(path) + file;
-							ImportTexture.run(file);
-							let ar = file.split(Path.sep);
-							let filename = ar[ar.length - 1];
-							n.buttons[0].default_value = Base.getAssetIndex(filename);
-						}
-						else if (search == "valtorgb") {
-							let ramp: any = BlHandle.get(node, "storage", 0, "ColorBand");
-							n.buttons[0].data = BlHandle.get(ramp, "ipotype") == 0 ? 0 : 1; // Linear / Constant
-							let elems: f32[][] = n.buttons[0].default_value;
-							for (let i = 0; i < BlHandle.get(ramp, "tot"); ++i) {
-								if (i >= elems.length) elems.push([1.0, 1.0, 1.0, 1.0, 0.0]);
-								let cbdata: any = BlHandle.get(ramp, "data", i, "CBData");
-								elems[i][0] = Math.floor(BlHandle.get(cbdata, "r") * 100) / 100;
-								elems[i][1] = Math.floor(BlHandle.get(cbdata, "g") * 100) / 100;
-								elems[i][2] = Math.floor(BlHandle.get(cbdata, "b") * 100) / 100;
-								elems[i][3] = Math.floor(BlHandle.get(cbdata, "a") * 100) / 100;
-								elems[i][4] = Math.floor(BlHandle.get(cbdata, "pos") * 100) / 100;
-							}
-						}
-						else if (search == "mixrgb" || search == "math") {
-							n.buttons[0].default_value = BlHandle.get(node, "custom1");
-							n.buttons[1].default_value = BlHandle.get(node, "custom2") & 2;
-						}
-						else if (search == "mapping") {
-							let storage = BlHandle.get(node, "storage", 0, "TexMapping");
-							n.buttons[0].default_value = BlHandle.get(storage, "loc");
-							n.buttons[1].default_value = BlHandle.get(storage, "rot");
-							n.buttons[2].default_value = BlHandle.get(storage, "size");
-							// let mat = BlHandle.get(storage, "mat"); float[4][4]
-							// storage.flag & 1 // use_min
-							// storage.flag & 2 // use_max
-							// storage.min[0]
-							// storage.min[1]
-							// storage.min[2]
-							// storage.max[0]
-							// storage.max[1]
-							// storage.max[2]
+					// Fill button values
+					if (search == "teximage") {
+						let img = BlHandle.get(node, "id", 0, "Image");
+						let file: string = BlHandle.get(img, "name").substr(2); // '//desktop\logo.png'
+						file = Path.baseDir(path) + file;
+						ImportTexture.run(file);
+						let ar = file.split(Path.sep);
+						let filename = ar[ar.length - 1];
+						n.buttons[0].default_value = Base.getAssetIndex(filename);
+					}
+					else if (search == "valtorgb") {
+						let ramp: any = BlHandle.get(node, "storage", 0, "ColorBand");
+						n.buttons[0].data = BlHandle.get(ramp, "ipotype") == 0 ? 0 : 1; // Linear / Constant
+						let elems: f32[][] = n.buttons[0].default_value;
+						for (let i = 0; i < BlHandle.get(ramp, "tot"); ++i) {
+							if (i >= elems.length) elems.push([1.0, 1.0, 1.0, 1.0, 0.0]);
+							let cbdata: any = BlHandle.get(ramp, "data", i, "CBData");
+							elems[i][0] = Math.floor(BlHandle.get(cbdata, "r") * 100) / 100;
+							elems[i][1] = Math.floor(BlHandle.get(cbdata, "g") * 100) / 100;
+							elems[i][2] = Math.floor(BlHandle.get(cbdata, "b") * 100) / 100;
+							elems[i][3] = Math.floor(BlHandle.get(cbdata, "a") * 100) / 100;
+							elems[i][4] = Math.floor(BlHandle.get(cbdata, "pos") * 100) / 100;
 						}
+					}
+					else if (search == "mixrgb" || search == "math") {
+						n.buttons[0].default_value = BlHandle.get(node, "custom1");
+						n.buttons[1].default_value = BlHandle.get(node, "custom2") & 2;
+					}
+					else if (search == "mapping") {
+						let storage = BlHandle.get(node, "storage", 0, "TexMapping");
+						n.buttons[0].default_value = BlHandle.get(storage, "loc");
+						n.buttons[1].default_value = BlHandle.get(storage, "rot");
+						n.buttons[2].default_value = BlHandle.get(storage, "size");
+						// let mat = BlHandle.get(storage, "mat"); float[4][4]
+						// storage.flag & 1 // use_min
+						// storage.flag & 2 // use_max
+						// storage.min[0]
+						// storage.min[1]
+						// storage.min[2]
+						// storage.max[0]
+						// storage.max[1]
+						// storage.max[2]
+					}
 
-						// Fill output socket values
-						let outputs = BlHandle.get(node, "outputs");
-						sock = BlHandle.get(outputs, "first", 0, "bNodeSocket");
-						pos = 0;
-						while (true) {
-							if (pos >= n.outputs.length) break;
-							n.outputs[pos].default_value = ImportBlendMaterial.readBlendSocket(sock);
-
-							let last = sock;
-							sock = BlHandle.get(sock, "next");
-							if (last.block == sock.block) break;
-							pos++;
-						}
+					// Fill output socket values
+					let outputs = BlHandle.get(node, "outputs");
+					sock = BlHandle.get(outputs, "first", 0, "bNodeSocket");
+					pos = 0;
+					while (true) {
+						if (pos >= n.outputs.length) break;
+						n.outputs[pos].default_value = ImportBlendMaterial.readBlendSocket(sock);
 
-						canvas.nodes.push(n);
+						let last = sock;
+						sock = BlHandle.get(sock, "next");
+						if (last.block == sock.block) break;
+						pos++;
 					}
 
-					if (BlHandle.get(node, "name") == BlHandle.get(last, "name")) break;
-					node = BlHandle.get(node, "next");
+					canvas.nodes.push(n);
 				}
 
-				// Place links
-				let link: any = BlHandle.get(bllinks, "first", 0, "bNodeLink");
-				while (true) {
-					let fromnode = BlHandle.get(BlHandle.get(link, "fromnode"), "name");
-					let tonode = BlHandle.get(BlHandle.get(link, "tonode"), "name");
-					let fromsock = BlHandle.get(link, "fromsock");
-					let tosock = BlHandle.get(link, "tosock");
+				if (BlHandle.get(node, "name") == BlHandle.get(last, "name")) break;
+				node = BlHandle.get(node, "next");
+			}
 
-					let from_id = -1;
-					let to_id = -1;
-					for (let n of canvas.nodes) {
-						if (n.name == fromnode) {
-							from_id = n.id;
-							break;
-						}
+			// Place links
+			let link: any = BlHandle.get(bllinks, "first", 0, "bNodeLink");
+			while (true) {
+				let fromnode = BlHandle.get(BlHandle.get(link, "fromnode"), "name");
+				let tonode = BlHandle.get(BlHandle.get(link, "tonode"), "name");
+				let fromsock = BlHandle.get(link, "fromsock");
+				let tosock = BlHandle.get(link, "tosock");
+
+				let from_id = -1;
+				let to_id = -1;
+				for (let n of canvas.nodes) {
+					if (n.name == fromnode) {
+						from_id = n.id;
+						break;
 					}
-					for (let n of canvas.nodes) {
-						if (n.name == tonode) {
-							to_id = n.id;
-							break;
-						}
+				}
+				for (let n of canvas.nodes) {
+					if (n.name == tonode) {
+						to_id = n.id;
+						break;
 					}
+				}
 
-					if (from_id >= 0 && to_id >= 0) {
-						let from_socket = 0;
-						let sock: any = fromsock;
-						while (true) {
-							let last = sock;
-							sock = BlHandle.get(sock, "prev");
-							if (last.block == sock.block) break;
-							from_socket++;
-						}
-
-						let to_socket = 0;
-						sock = tosock;
-						while (true) {
-							let last = sock;
-							sock = BlHandle.get(sock, "prev");
-							if (last.block == sock.block) break;
-							to_socket++;
-						}
+				if (from_id >= 0 && to_id >= 0) {
+					let from_socket = 0;
+					let sock: any = fromsock;
+					while (true) {
+						let last = sock;
+						sock = BlHandle.get(sock, "prev");
+						if (last.block == sock.block) break;
+						from_socket++;
+					}
 
-						let valid = true;
+					let to_socket = 0;
+					sock = tosock;
+					while (true) {
+						let last = sock;
+						sock = BlHandle.get(sock, "prev");
+						if (last.block == sock.block) break;
+						to_socket++;
+					}
 
-						// Remap principled
-						if (tonode == nout.name) {
-							if (to_socket == 0) to_socket = 0; // Base
-							else if (to_socket == 18) to_socket = 1; // Opac
-							else if (to_socket == 7) to_socket = 3; // Rough
-							else if (to_socket == 4) to_socket = 4; // Met
-							else if (to_socket == 19) to_socket = 5; // TODO: auto-remove normal_map node
-							else if (to_socket == 17) to_socket = 6; // Emis
-							else if (to_socket == 1) to_socket = 8; // Subs
-							else valid = false;
-						}
+					let valid = true;
 
-						if (valid) {
-							let raw: zui_node_link_t = {
-								id: zui_get_link_id(canvas.links),
-								from_id: from_id,
-								from_socket: from_socket,
-								to_id: to_id,
-								to_socket: to_socket
-							};
-							canvas.links.push(raw);
-						}
+					// Remap principled
+					if (tonode == nout.name) {
+						if (to_socket == 0) to_socket = 0; // Base
+						else if (to_socket == 18) to_socket = 1; // Opac
+						else if (to_socket == 7) to_socket = 3; // Rough
+						else if (to_socket == 4) to_socket = 4; // Met
+						else if (to_socket == 19) to_socket = 5; // TODO: auto-remove normal_map node
+						else if (to_socket == 17) to_socket = 6; // Emis
+						else if (to_socket == 1) to_socket = 8; // Subs
+						else valid = false;
 					}
 
-					let last = link;
-					link = BlHandle.get(link, "next");
-					if (last.block == link.block) break;
+					if (valid) {
+						let raw: zui_node_link_t = {
+							id: zui_get_link_id(canvas.links),
+							from_id: from_id,
+							from_socket: from_socket,
+							to_id: to_id,
+							to_socket: to_socket
+						};
+						canvas.links.push(raw);
+					}
 				}
-				History.newMaterial();
+
+				let last = link;
+				link = BlHandle.get(link, "next");
+				if (last.block == link.block) break;
 			}
+			History.newMaterial();
+		}
 
-			let _init = () => {
-				for (let m of imported) {
-					Context.setMaterial(m);
-					MakeMaterial.parsePaintMaterial();
-					UtilRender.makeMaterialPreview();
-				}
+		let _init = () => {
+			for (let m of imported) {
+				Context.setMaterial(m);
+				MakeMaterial.parsePaintMaterial();
+				UtilRender.makeMaterialPreview();
 			}
-			app_notify_on_init(_init);
+		}
+		app_notify_on_init(_init);
 
-			UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
-			data_delete_blob(path);
-		});
+		UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
+		data_delete_blob(path);
 	}
 
 	static readBlendSocket = (sock: any): any => {

+ 354 - 355
base/Sources/ImportBlendMesh.ts

@@ -4,139 +4,295 @@ class ImportBlendMesh {
 	static eps = 1.0 / 32767;
 
 	static run = (path: string, replaceExisting = true) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			let bl = ParserBlend.init(b);
-			if (bl.dna == null) {
-				Console.error(Strings.error3());
-				return;
-			}
+		let b: ArrayBuffer = data_get_blob(path);
+		let bl = ParserBlend.init(b);
+		if (bl.dna == null) {
+			Console.error(Strings.error3());
+			return;
+		}
 
-			let obs = ParserBlend.get(bl, "Object");
-			if (obs == null || obs.length == 0) { ImportMesh.makeMesh(null, path); return; }
+		let obs = ParserBlend.get(bl, "Object");
+		if (obs == null || obs.length == 0) { ImportMesh.makeMesh(null, path); return; }
 
-			let first = true;
-			for (let ob of obs) {
-				if (BlHandle.get(ob, "type") != 1) continue;
+		let first = true;
+		for (let ob of obs) {
+			if (BlHandle.get(ob, "type") != 1) continue;
 
-				let name: string = BlHandle.get(BlHandle.get(ob, "id"), "name");
-				name = name.substring(2, name.length);
+			let name: string = BlHandle.get(BlHandle.get(ob, "id"), "name");
+			name = name.substring(2, name.length);
 
-				let m: any = BlHandle.get(ob, "data", 0, "Mesh");
-				if (m == null) continue;
+			let m: any = BlHandle.get(ob, "data", 0, "Mesh");
+			if (m == null) continue;
 
-				let totpoly = BlHandle.get(m, "totpoly");
-				if (totpoly == 0) continue;
+			let totpoly = BlHandle.get(m, "totpoly");
+			if (totpoly == 0) continue;
 
-				let numtri = 0;
-				for (let i = 0; i < totpoly; ++i) {
-					let poly = BlHandle.get(m, "mpoly", i);
-					let totloop = BlHandle.get(poly, "totloop");
-					numtri += totloop - 2;
-				}
-				let inda = new Uint32Array(numtri * 3);
-				for (let i = 0; i < inda.length; ++i) inda[i] = i;
+			let numtri = 0;
+			for (let i = 0; i < totpoly; ++i) {
+				let poly = BlHandle.get(m, "mpoly", i);
+				let totloop = BlHandle.get(poly, "totloop");
+				numtri += totloop - 2;
+			}
+			let inda = new Uint32Array(numtri * 3);
+			for (let i = 0; i < inda.length; ++i) inda[i] = i;
 
-				let posa32 = new Float32Array(numtri * 3 * 4);
-				let posa = new Int16Array(numtri * 3 * 4);
-				let nora = new Int16Array(numtri * 3 * 2);
+			let posa32 = new Float32Array(numtri * 3 * 4);
+			let posa = new Int16Array(numtri * 3 * 4);
+			let nora = new Int16Array(numtri * 3 * 2);
 
-				// pdata, 25 == CD_MPOLY
-				// let vdata: any = BlHandle.get(m, "vdata");
-				// let codata: any = null;
-				// let codata_pos = 0;
-				// for (let i = 0; i < BlHandle.get(vdata, "totlayer"); ++i) {
-				// 	let l = BlHandle.get(vdata, "layers", i);
-				// 	if (BlHandle.get(l, "type") == 0) { // CD_MVERT
-				// 		let ptr: any = BlHandle.get(l, "data");
-				// 		codata_pos = bl.BlHandle.get(map, ptr).pos;
-				// 		codata = l;
-				// 	}
-				// }
+			// pdata, 25 == CD_MPOLY
+			// let vdata: any = BlHandle.get(m, "vdata");
+			// let codata: any = null;
+			// let codata_pos = 0;
+			// for (let i = 0; i < BlHandle.get(vdata, "totlayer"); ++i) {
+			// 	let l = BlHandle.get(vdata, "layers", i);
+			// 	if (BlHandle.get(l, "type") == 0) { // CD_MVERT
+			// 		let ptr: any = BlHandle.get(l, "data");
+			// 		codata_pos = bl.BlHandle.get(map, ptr).pos;
+			// 		codata = l;
+			// 	}
+			// }
 
-				let ldata: any = BlHandle.get(m, "ldata");
-				let uvdata: any = null;
-				let uvdata_pos = 0;
-				let coldata: any = null;
-				let coldata_pos = 0;
+			let ldata: any = BlHandle.get(m, "ldata");
+			let uvdata: any = null;
+			let uvdata_pos = 0;
+			let coldata: any = null;
+			let coldata_pos = 0;
 
-				for (let i = 0; i < BlHandle.get(ldata, "totlayer"); ++i) {
-					let l = BlHandle.get(ldata, "layers", i);
-					if (BlHandle.get(l, "type") == 16) { // CD_MLOOPUV
-						let ptr: any = BlHandle.get(l, "data");
-						uvdata_pos = bl.map.get(ptr).pos;
-						uvdata = l;
-					}
-					else if (BlHandle.get(l, "type") == 17) { // CD_PROP_BYTE_COLOR
-						let ptr: any = BlHandle.get(l, "data");
-						coldata_pos = bl.map.get(ptr).pos;
-						coldata = l;
-					}
-					// CD_MLOOP == 26
+			for (let i = 0; i < BlHandle.get(ldata, "totlayer"); ++i) {
+				let l = BlHandle.get(ldata, "layers", i);
+				if (BlHandle.get(l, "type") == 16) { // CD_MLOOPUV
+					let ptr: any = BlHandle.get(l, "data");
+					uvdata_pos = bl.map.get(ptr).pos;
+					uvdata = l;
 				}
+				else if (BlHandle.get(l, "type") == 17) { // CD_PROP_BYTE_COLOR
+					let ptr: any = BlHandle.get(l, "data");
+					coldata_pos = bl.map.get(ptr).pos;
+					coldata = l;
+				}
+				// CD_MLOOP == 26
+			}
 
-				let hasuv = uvdata != null;
-				let texa = hasuv ? new Int16Array(numtri * 3 * 2) : null;
-				let hascol = Context.raw.parseVCols && coldata != null;
-				let cola = hascol ? new Int16Array(numtri * 3 * 3) : null;
+			let hasuv = uvdata != null;
+			let texa = hasuv ? new Int16Array(numtri * 3 * 2) : null;
+			let hascol = Context.raw.parseVCols && coldata != null;
+			let cola = hascol ? new Int16Array(numtri * 3 * 3) : null;
 
-				let tri = 0;
-				let vec0 = vec4_create();
-				let vec1 = vec4_create();
-				let vec2 = vec4_create();
-				for (let i = 0; i < totpoly; ++i) {
-					let poly = BlHandle.get(m, "mpoly", i);
-					// let smooth = BlHandle.get(poly, "flag") & 1 == 1; // ME_SMOOTH
-					let smooth = false; // TODO: fetch smooth normals
-					let loopstart = BlHandle.get(poly, "loopstart");
-					let totloop = BlHandle.get(poly, "totloop");
-					if (totloop <= 4) { // Convex, fan triangulation
-						let v0 = ImportBlendMesh.get_mvert_v(m, loopstart + totloop - 1);
-						let v1 = ImportBlendMesh.get_mvert_v(m, loopstart);
-						let co0 = BlHandle.get(v0, "co");
-						let co1 = BlHandle.get(v1, "co");
-						let no0 = BlHandle.get(v0, "no");
-						let no1 = BlHandle.get(v1, "no");
+			let tri = 0;
+			let vec0 = vec4_create();
+			let vec1 = vec4_create();
+			let vec2 = vec4_create();
+			for (let i = 0; i < totpoly; ++i) {
+				let poly = BlHandle.get(m, "mpoly", i);
+				// let smooth = BlHandle.get(poly, "flag") & 1 == 1; // ME_SMOOTH
+				let smooth = false; // TODO: fetch smooth normals
+				let loopstart = BlHandle.get(poly, "loopstart");
+				let totloop = BlHandle.get(poly, "totloop");
+				if (totloop <= 4) { // Convex, fan triangulation
+					let v0 = ImportBlendMesh.get_mvert_v(m, loopstart + totloop - 1);
+					let v1 = ImportBlendMesh.get_mvert_v(m, loopstart);
+					let co0 = BlHandle.get(v0, "co");
+					let co1 = BlHandle.get(v1, "co");
+					let no0 = BlHandle.get(v0, "no");
+					let no1 = BlHandle.get(v1, "no");
+					if (smooth) {
+						vec4_normalize(vec4_set(vec0, no0[0] / 32767, no0[1] / 32767, no0[2] / 32767)); // shortmax
+						vec4_normalize(vec4_set(vec1, no1[0] / 32767, no1[1] / 32767, no1[2] / 32767));
+					}
+					let uv0: Float32Array = null;
+					let uv1: Float32Array = null;
+					let uv2: Float32Array = null;
+					if (hasuv) {
+						bl.pos = uvdata_pos + (loopstart + totloop - 1) * 4 * 3; // * 3 = x, y, flag
+						uv0 = ParserBlend.readf32array(bl, 2);
+						if (uv0[0] > 1.0 + ImportBlendMesh.eps) uv0[0] = uv0[0] - Math.floor(uv0[0]);
+						if (uv0[1] > 1.0 + ImportBlendMesh.eps) uv0[1] = uv0[1] - Math.floor(uv0[1]);
+						bl.pos = uvdata_pos + (loopstart) * 4 * 3;
+						uv1 = ParserBlend.readf32array(bl, 2);
+						if (uv1[0] > 1.0 + ImportBlendMesh.eps) uv1[0] = uv1[0] - Math.floor(uv1[0]);
+						if (uv1[1] > 1.0 + ImportBlendMesh.eps) uv1[1] = uv1[1] - Math.floor(uv1[1]);
+					}
+					let col0r: i32 = 0;
+					let col0g: i32 = 0;
+					let col0b: i32 = 0;
+					let col1r: i32 = 0;
+					let col1g: i32 = 0;
+					let col1b: i32 = 0;
+					let col2r: i32 = 0;
+					let col2g: i32 = 0;
+					let col2b: i32 = 0;
+					if (hascol) {
+						bl.pos = coldata_pos + (loopstart + totloop - 1) * 1 * 4; // * 4 = r, g, b, a
+						col0r = ParserBlend.read8(bl);
+						col0g = ParserBlend.read8(bl);
+						col0b = ParserBlend.read8(bl);
+						bl.pos = coldata_pos + (loopstart) * 1 * 4;
+						col1r = ParserBlend.read8(bl);
+						col1g = ParserBlend.read8(bl);
+						col1b = ParserBlend.read8(bl);
+					}
+					for (let j = 0; j < totloop - 2; ++j) {
+						let v2 = ImportBlendMesh.get_mvert_v(m, loopstart + j + 1);
+						let co2 = BlHandle.get(v2, "co");
+						let no2 = BlHandle.get(v2, "no");
 						if (smooth) {
-							vec4_normalize(vec4_set(vec0, no0[0] / 32767, no0[1] / 32767, no0[2] / 32767)); // shortmax
-							vec4_normalize(vec4_set(vec1, no1[0] / 32767, no1[1] / 32767, no1[2] / 32767));
+							vec4_normalize(vec4_set(vec2, no2[0] / 32767, no2[1] / 32767, no2[2] / 32767));
+						}
+						else {
+							vec4_set(vec2, co2[0], co2[1], co2[2]);
+							vec4_set(vec1, co1[0], co1[1], co1[2]);
+							vec4_sub_vecs(vec0, vec2, vec1);
+							vec4_set(vec2, co0[0], co0[1], co0[2]);
+							vec4_sub_vecs(vec1, vec2, vec1);
+							vec4_cross(vec0, vec1);
+							vec4_normalize(vec0);
 						}
-						let uv0: Float32Array = null;
-						let uv1: Float32Array = null;
-						let uv2: Float32Array = null;
+						posa32[tri * 9    ] = co0[0];
+						posa32[tri * 9 + 1] = co0[1];
+						posa32[tri * 9 + 2] = co0[2];
+						posa32[tri * 9 + 3] = co1[0];
+						posa32[tri * 9 + 4] = co1[1];
+						posa32[tri * 9 + 5] = co1[2];
+						posa32[tri * 9 + 6] = co2[0];
+						posa32[tri * 9 + 7] = co2[1];
+						posa32[tri * 9 + 8] = co2[2];
+						posa[tri * 12 + 3] = Math.floor(vec0.z * 32767);
+						posa[tri * 12 + 7] = Math.floor((smooth ? vec1.z : vec0.z) * 32767);
+						posa[tri * 12 + 11] = Math.floor((smooth ? vec2.z : vec0.z) * 32767);
+						nora[tri * 6    ] = Math.floor(vec0.x * 32767);
+						nora[tri * 6 + 1] = Math.floor(vec0.y * 32767);
+						nora[tri * 6 + 2] = Math.floor((smooth ? vec1.x : vec0.x) * 32767);
+						nora[tri * 6 + 3] = Math.floor((smooth ? vec1.y : vec0.y) * 32767);
+						nora[tri * 6 + 4] = Math.floor((smooth ? vec2.x : vec0.x) * 32767);
+						nora[tri * 6 + 5] = Math.floor((smooth ? vec2.y : vec0.y) * 32767);
+						co1 = co2;
+						no1 = no2;
+						vec4_set_from(vec1, vec2);
 						if (hasuv) {
-							bl.pos = uvdata_pos + (loopstart + totloop - 1) * 4 * 3; // * 3 = x, y, flag
-							uv0 = ParserBlend.readf32array(bl, 2);
-							if (uv0[0] > 1.0 + ImportBlendMesh.eps) uv0[0] = uv0[0] - Math.floor(uv0[0]);
-							if (uv0[1] > 1.0 + ImportBlendMesh.eps) uv0[1] = uv0[1] - Math.floor(uv0[1]);
-							bl.pos = uvdata_pos + (loopstart) * 4 * 3;
-							uv1 = ParserBlend.readf32array(bl, 2);
-							if (uv1[0] > 1.0 + ImportBlendMesh.eps) uv1[0] = uv1[0] - Math.floor(uv1[0]);
-							if (uv1[1] > 1.0 + ImportBlendMesh.eps) uv1[1] = uv1[1] - Math.floor(uv1[1]);
+							bl.pos = uvdata_pos + (loopstart + j + 1) * 4 * 3;
+							uv2 = ParserBlend.readf32array(bl, 2);
+							if (uv2[0] > 1.0 + ImportBlendMesh.eps) uv2[0] = uv2[0] - Math.floor(uv2[0]);
+							if (uv2[1] > 1.0 + ImportBlendMesh.eps) uv2[1] = uv2[1] - Math.floor(uv2[1]);
+							texa[tri * 6    ] = Math.floor(uv0[0] * 32767);
+							texa[tri * 6 + 1] = Math.floor((1.0 - uv0[1]) * 32767);
+							texa[tri * 6 + 2] = Math.floor(uv1[0] * 32767);
+							texa[tri * 6 + 3] = Math.floor((1.0 - uv1[1]) * 32767);
+							texa[tri * 6 + 4] = Math.floor(uv2[0] * 32767);
+							texa[tri * 6 + 5] = Math.floor((1.0 - uv2[1]) * 32767);
+							uv1 = uv2;
 						}
-						let col0r: i32 = 0;
-						let col0g: i32 = 0;
-						let col0b: i32 = 0;
-						let col1r: i32 = 0;
-						let col1g: i32 = 0;
-						let col1b: i32 = 0;
-						let col2r: i32 = 0;
-						let col2g: i32 = 0;
-						let col2b: i32 = 0;
 						if (hascol) {
-							bl.pos = coldata_pos + (loopstart + totloop - 1) * 1 * 4; // * 4 = r, g, b, a
-							col0r = ParserBlend.read8(bl);
-							col0g = ParserBlend.read8(bl);
-							col0b = ParserBlend.read8(bl);
-							bl.pos = coldata_pos + (loopstart) * 1 * 4;
-							col1r = ParserBlend.read8(bl);
-							col1g = ParserBlend.read8(bl);
-							col1b = ParserBlend.read8(bl);
+							bl.pos = coldata_pos + (loopstart + j + 1) * 1 * 4;
+							col2r = ParserBlend.read8(bl);
+							col2g = ParserBlend.read8(bl);
+							col2b = ParserBlend.read8(bl);
+							cola[tri * 9    ] = col0r * 128;
+							cola[tri * 9 + 1] = col0g * 128;
+							cola[tri * 9 + 2] = col0b * 128;
+							cola[tri * 9 + 3] = col1r * 128;
+							cola[tri * 9 + 4] = col1g * 128;
+							cola[tri * 9 + 5] = col1b * 128;
+							cola[tri * 9 + 6] = col2r * 128;
+							cola[tri * 9 + 7] = col2g * 128;
+							cola[tri * 9 + 8] = col2b * 128;
+							col1r = col2r;
+							col1g = col2g;
+							col1b = col2b;
+						}
+						tri++;
+					}
+				}
+				else { // Convex or concave, ear clipping
+					let va: i32[] = [];
+					for (let i = 0; i < totloop; ++i) va.push(loopstart + i);
+					let v0 = ImportBlendMesh.get_mvert_v(m, loopstart);
+					let v1 = ImportBlendMesh.get_mvert_v(m, loopstart + 1);
+					let v2 = ImportBlendMesh.get_mvert_v(m, loopstart + 2);
+					let co0 = BlHandle.get(v0, "co");
+					let co1 = BlHandle.get(v1, "co");
+					let co2 = BlHandle.get(v2, "co");
+					vec4_set(vec2, co2[0], co2[1], co2[2]);
+					vec4_set(vec1, co1[0], co1[1], co1[2]);
+					vec4_sub_vecs(vec0, vec2, vec1);
+					vec4_set(vec2, co0[0], co0[1], co0[2]);
+					vec4_sub_vecs(vec1, vec2, vec1);
+					vec4_cross(vec0, vec1);
+					vec4_normalize(vec0, );
+
+					let nx = vec0.x;
+					let ny = vec0.y;
+					let nz = vec0.z;
+					let nxabs = Math.abs(nx);
+					let nyabs = Math.abs(ny);
+					let nzabs = Math.abs(nz);
+					let flip = nx + ny + nz > 0;
+					let axis = nxabs > nyabs && nxabs > nzabs ? 0 : nyabs > nxabs && nyabs > nzabs ? 1 : 2;
+					let axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0);
+					let axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1);
+
+					let winding = 0.0;
+					for (let i = 0; i < totloop; ++i) {
+						let v0 = ImportBlendMesh.get_mvert_v(m, loopstart + i);
+						let v1 = ImportBlendMesh.get_mvert_v(m, loopstart + ((i + 1) % totloop));
+						let co0 = BlHandle.get(v0, "co");
+						let co1 = BlHandle.get(v1, "co");
+						winding += (co1[axis0] - co0[axis0]) * (co1[axis1] + co0[axis1]);
+					}
+					flip = winding > 0 ? nx + ny + nz > 0 : nx + ny + nz < 0;
+					axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0);
+					axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1);
+
+					let vi = totloop;
+					let loops = 0;
+					let i = -1;
+					while (vi > 2 && loops++ < vi) {
+						i = (i + 1) % vi;
+						let i1 = (i + 1) % vi;
+						let i2 = (i + 2) % vi;
+						let v0 = ImportBlendMesh.get_mvert_v(m, va[i ]);
+						let v1 = ImportBlendMesh.get_mvert_v(m, va[i1]);
+						let v2 = ImportBlendMesh.get_mvert_v(m, va[i2]);
+						let co0 = BlHandle.get(v0, "co");
+						let co1 = BlHandle.get(v1, "co");
+						let co2 = BlHandle.get(v2, "co");
+						let v0x = co0[axis0];
+						let v0y = co0[axis1];
+						let v1x = co1[axis0];
+						let v1y = co1[axis1];
+						let v2x = co2[axis0];
+						let v2y = co2[axis1];
+
+						let e0x = v0x - v1x; // Not an interior vertex
+						let e0y = v0y - v1y;
+						let e1x = v2x - v1x;
+						let e1y = v2y - v1y;
+						let cross = e0x * e1y - e0y * e1x;
+						if (cross <= 0) continue;
+
+						let overlap = false; // Other vertex found inside this triangle
+						for (let j = 0; j < vi - 3; ++j) {
+							let j0 = (i + 3 + j) % vi;
+							let v = ImportBlendMesh.get_mvert_v(m, va[j0]);
+							let co = BlHandle.get(v, "co");
+							let px = co[axis0];
+							let py = co[axis1];
+
+							if (UtilMesh.pnpoly(v0x, v0y, v1x, v1y, v2x, v2y, px, py)) {
+								overlap = true;
+								break;
+							}
 						}
-						for (let j = 0; j < totloop - 2; ++j) {
-							let v2 = ImportBlendMesh.get_mvert_v(m, loopstart + j + 1);
-							let co2 = BlHandle.get(v2, "co");
+						if (overlap) continue;
+
+						// Found ear
+						{
+							let no0 = BlHandle.get(v0, "no");
+							let no1 = BlHandle.get(v1, "no");
 							let no2 = BlHandle.get(v2, "no");
 							if (smooth) {
+								vec4_normalize(vec4_set(vec0, no0[0] / 32767, no0[1] / 32767, no0[2] / 32767)); // shortmax
+								vec4_normalize(vec4_set(vec1, no1[0] / 32767, no1[1] / 32767, no1[2] / 32767));
 								vec4_normalize(vec4_set(vec2, no2[0] / 32767, no2[1] / 32767, no2[2] / 32767));
 							}
 							else {
@@ -146,7 +302,47 @@ class ImportBlendMesh {
 								vec4_set(vec2, co0[0], co0[1], co0[2]);
 								vec4_sub_vecs(vec1, vec2, vec1);
 								vec4_cross(vec0, vec1);
-								vec4_normalize(vec0);
+								vec4_normalize(vec0, );
+							}
+							let uv0: Float32Array = null;
+							let uv1: Float32Array = null;
+							let uv2: Float32Array = null;
+							if (hasuv) {
+								bl.pos = uvdata_pos + (va[i ]) * 4 * 3;
+								uv0 = ParserBlend.readf32array(bl, 2);
+								if (uv0[0] > 1.0 + ImportBlendMesh.eps) uv0[0] = uv0[0] - Math.floor(uv0[0]);
+								if (uv0[1] > 1.0 + ImportBlendMesh.eps) uv0[1] = uv0[1] - Math.floor(uv0[1]);
+								bl.pos = uvdata_pos + (va[i1]) * 4 * 3;
+								uv1 = ParserBlend.readf32array(bl, 2);
+								if (uv1[0] > 1.0 + ImportBlendMesh.eps) uv1[0] = uv1[0] - Math.floor(uv1[0]);
+								if (uv1[1] > 1.0 + ImportBlendMesh.eps) uv1[1] = uv1[1] - Math.floor(uv1[1]);
+								bl.pos = uvdata_pos + (va[i2]) * 4 * 3;
+								uv2 = ParserBlend.readf32array(bl, 2);
+								if (uv2[0] > 1.0 + ImportBlendMesh.eps) uv2[0] = uv2[0] - Math.floor(uv2[0]);
+								if (uv2[1] > 1.0 + ImportBlendMesh.eps) uv2[1] = uv2[1] - Math.floor(uv2[1]);
+							}
+							let col0r: i32 = 0;
+							let col0g: i32 = 0;
+							let col0b: i32 = 0;
+							let col1r: i32 = 0;
+							let col1g: i32 = 0;
+							let col1b: i32 = 0;
+							let col2r: i32 = 0;
+							let col2g: i32 = 0;
+							let col2b: i32 = 0;
+							if (hascol) {
+								bl.pos = coldata_pos + (va[i ]) * 1 * 4;
+								col0r = ParserBlend.read8(bl);
+								col0g = ParserBlend.read8(bl);
+								col0b = ParserBlend.read8(bl);
+								bl.pos = coldata_pos + (va[i1]) * 1 * 4;
+								col1r = ParserBlend.read8(bl);
+								col1g = ParserBlend.read8(bl);
+								col1b = ParserBlend.read8(bl);
+								bl.pos = coldata_pos + (va[i2]) * 1 * 4;
+								col2r = ParserBlend.read8(bl);
+								col2g = ParserBlend.read8(bl);
+								col2b = ParserBlend.read8(bl);
 							}
 							posa32[tri * 9    ] = co0[0];
 							posa32[tri * 9 + 1] = co0[1];
@@ -166,27 +362,15 @@ class ImportBlendMesh {
 							nora[tri * 6 + 3] = Math.floor((smooth ? vec1.y : vec0.y) * 32767);
 							nora[tri * 6 + 4] = Math.floor((smooth ? vec2.x : vec0.x) * 32767);
 							nora[tri * 6 + 5] = Math.floor((smooth ? vec2.y : vec0.y) * 32767);
-							co1 = co2;
-							no1 = no2;
-							vec4_set_from(vec1, vec2);
 							if (hasuv) {
-								bl.pos = uvdata_pos + (loopstart + j + 1) * 4 * 3;
-								uv2 = ParserBlend.readf32array(bl, 2);
-								if (uv2[0] > 1.0 + ImportBlendMesh.eps) uv2[0] = uv2[0] - Math.floor(uv2[0]);
-								if (uv2[1] > 1.0 + ImportBlendMesh.eps) uv2[1] = uv2[1] - Math.floor(uv2[1]);
 								texa[tri * 6    ] = Math.floor(uv0[0] * 32767);
 								texa[tri * 6 + 1] = Math.floor((1.0 - uv0[1]) * 32767);
 								texa[tri * 6 + 2] = Math.floor(uv1[0] * 32767);
 								texa[tri * 6 + 3] = Math.floor((1.0 - uv1[1]) * 32767);
 								texa[tri * 6 + 4] = Math.floor(uv2[0] * 32767);
 								texa[tri * 6 + 5] = Math.floor((1.0 - uv2[1]) * 32767);
-								uv1 = uv2;
 							}
 							if (hascol) {
-								bl.pos = coldata_pos + (loopstart + j + 1) * 1 * 4;
-								col2r = ParserBlend.read8(bl);
-								col2g = ParserBlend.read8(bl);
-								col2b = ParserBlend.read8(bl);
 								cola[tri * 9    ] = col0r * 128;
 								cola[tri * 9 + 1] = col0g * 128;
 								cola[tri * 9 + 2] = col0b * 128;
@@ -196,248 +380,63 @@ class ImportBlendMesh {
 								cola[tri * 9 + 6] = col2r * 128;
 								cola[tri * 9 + 7] = col2g * 128;
 								cola[tri * 9 + 8] = col2b * 128;
-								col1r = col2r;
-								col1g = col2g;
-								col1b = col2b;
 							}
 							tri++;
 						}
-					}
-					else { // Convex or concave, ear clipping
-						let va: i32[] = [];
-						for (let i = 0; i < totloop; ++i) va.push(loopstart + i);
-						let v0 = ImportBlendMesh.get_mvert_v(m, loopstart);
-						let v1 = ImportBlendMesh.get_mvert_v(m, loopstart + 1);
-						let v2 = ImportBlendMesh.get_mvert_v(m, loopstart + 2);
-						let co0 = BlHandle.get(v0, "co");
-						let co1 = BlHandle.get(v1, "co");
-						let co2 = BlHandle.get(v2, "co");
-						vec4_set(vec2, co2[0], co2[1], co2[2]);
-						vec4_set(vec1, co1[0], co1[1], co1[2]);
-						vec4_sub_vecs(vec0, vec2, vec1);
-						vec4_set(vec2, co0[0], co0[1], co0[2]);
-						vec4_sub_vecs(vec1, vec2, vec1);
-						vec4_cross(vec0, vec1);
-						vec4_normalize(vec0, );
-
-						let nx = vec0.x;
-						let ny = vec0.y;
-						let nz = vec0.z;
-						let nxabs = Math.abs(nx);
-						let nyabs = Math.abs(ny);
-						let nzabs = Math.abs(nz);
-						let flip = nx + ny + nz > 0;
-						let axis = nxabs > nyabs && nxabs > nzabs ? 0 : nyabs > nxabs && nyabs > nzabs ? 1 : 2;
-						let axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0);
-						let axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1);
-
-						let winding = 0.0;
-						for (let i = 0; i < totloop; ++i) {
-							let v0 = ImportBlendMesh.get_mvert_v(m, loopstart + i);
-							let v1 = ImportBlendMesh.get_mvert_v(m, loopstart + ((i + 1) % totloop));
-							let co0 = BlHandle.get(v0, "co");
-							let co1 = BlHandle.get(v1, "co");
-							winding += (co1[axis0] - co0[axis0]) * (co1[axis1] + co0[axis1]);
-						}
-						flip = winding > 0 ? nx + ny + nz > 0 : nx + ny + nz < 0;
-						axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0);
-						axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1);
 
-						let vi = totloop;
-						let loops = 0;
-						let i = -1;
-						while (vi > 2 && loops++ < vi) {
-							i = (i + 1) % vi;
-							let i1 = (i + 1) % vi;
-							let i2 = (i + 2) % vi;
-							let v0 = ImportBlendMesh.get_mvert_v(m, va[i ]);
-							let v1 = ImportBlendMesh.get_mvert_v(m, va[i1]);
-							let v2 = ImportBlendMesh.get_mvert_v(m, va[i2]);
-							let co0 = BlHandle.get(v0, "co");
-							let co1 = BlHandle.get(v1, "co");
-							let co2 = BlHandle.get(v2, "co");
-							let v0x = co0[axis0];
-							let v0y = co0[axis1];
-							let v1x = co1[axis0];
-							let v1y = co1[axis1];
-							let v2x = co2[axis0];
-							let v2y = co2[axis1];
-
-							let e0x = v0x - v1x; // Not an interior vertex
-							let e0y = v0y - v1y;
-							let e1x = v2x - v1x;
-							let e1y = v2y - v1y;
-							let cross = e0x * e1y - e0y * e1x;
-							if (cross <= 0) continue;
-
-							let overlap = false; // Other vertex found inside this triangle
-							for (let j = 0; j < vi - 3; ++j) {
-								let j0 = (i + 3 + j) % vi;
-								let v = ImportBlendMesh.get_mvert_v(m, va[j0]);
-								let co = BlHandle.get(v, "co");
-								let px = co[axis0];
-								let py = co[axis1];
-
-								if (UtilMesh.pnpoly(v0x, v0y, v1x, v1y, v2x, v2y, px, py)) {
-									overlap = true;
-									break;
-								}
-							}
-							if (overlap) continue;
-
-							// Found ear
-							{
-								let no0 = BlHandle.get(v0, "no");
-								let no1 = BlHandle.get(v1, "no");
-								let no2 = BlHandle.get(v2, "no");
-								if (smooth) {
-									vec4_normalize(vec4_set(vec0, no0[0] / 32767, no0[1] / 32767, no0[2] / 32767)); // shortmax
-									vec4_normalize(vec4_set(vec1, no1[0] / 32767, no1[1] / 32767, no1[2] / 32767));
-									vec4_normalize(vec4_set(vec2, no2[0] / 32767, no2[1] / 32767, no2[2] / 32767));
-								}
-								else {
-									vec4_set(vec2, co2[0], co2[1], co2[2]);
-									vec4_set(vec1, co1[0], co1[1], co1[2]);
-									vec4_sub_vecs(vec0, vec2, vec1);
-									vec4_set(vec2, co0[0], co0[1], co0[2]);
-									vec4_sub_vecs(vec1, vec2, vec1);
-									vec4_cross(vec0, vec1);
-									vec4_normalize(vec0, );
-								}
-								let uv0: Float32Array = null;
-								let uv1: Float32Array = null;
-								let uv2: Float32Array = null;
-								if (hasuv) {
-									bl.pos = uvdata_pos + (va[i ]) * 4 * 3;
-									uv0 = ParserBlend.readf32array(bl, 2);
-									if (uv0[0] > 1.0 + ImportBlendMesh.eps) uv0[0] = uv0[0] - Math.floor(uv0[0]);
-									if (uv0[1] > 1.0 + ImportBlendMesh.eps) uv0[1] = uv0[1] - Math.floor(uv0[1]);
-									bl.pos = uvdata_pos + (va[i1]) * 4 * 3;
-									uv1 = ParserBlend.readf32array(bl, 2);
-									if (uv1[0] > 1.0 + ImportBlendMesh.eps) uv1[0] = uv1[0] - Math.floor(uv1[0]);
-									if (uv1[1] > 1.0 + ImportBlendMesh.eps) uv1[1] = uv1[1] - Math.floor(uv1[1]);
-									bl.pos = uvdata_pos + (va[i2]) * 4 * 3;
-									uv2 = ParserBlend.readf32array(bl, 2);
-									if (uv2[0] > 1.0 + ImportBlendMesh.eps) uv2[0] = uv2[0] - Math.floor(uv2[0]);
-									if (uv2[1] > 1.0 + ImportBlendMesh.eps) uv2[1] = uv2[1] - Math.floor(uv2[1]);
-								}
-								let col0r: i32 = 0;
-								let col0g: i32 = 0;
-								let col0b: i32 = 0;
-								let col1r: i32 = 0;
-								let col1g: i32 = 0;
-								let col1b: i32 = 0;
-								let col2r: i32 = 0;
-								let col2g: i32 = 0;
-								let col2b: i32 = 0;
-								if (hascol) {
-									bl.pos = coldata_pos + (va[i ]) * 1 * 4;
-									col0r = ParserBlend.read8(bl);
-									col0g = ParserBlend.read8(bl);
-									col0b = ParserBlend.read8(bl);
-									bl.pos = coldata_pos + (va[i1]) * 1 * 4;
-									col1r = ParserBlend.read8(bl);
-									col1g = ParserBlend.read8(bl);
-									col1b = ParserBlend.read8(bl);
-									bl.pos = coldata_pos + (va[i2]) * 1 * 4;
-									col2r = ParserBlend.read8(bl);
-									col2g = ParserBlend.read8(bl);
-									col2b = ParserBlend.read8(bl);
-								}
-								posa32[tri * 9    ] = co0[0];
-								posa32[tri * 9 + 1] = co0[1];
-								posa32[tri * 9 + 2] = co0[2];
-								posa32[tri * 9 + 3] = co1[0];
-								posa32[tri * 9 + 4] = co1[1];
-								posa32[tri * 9 + 5] = co1[2];
-								posa32[tri * 9 + 6] = co2[0];
-								posa32[tri * 9 + 7] = co2[1];
-								posa32[tri * 9 + 8] = co2[2];
-								posa[tri * 12 + 3] = Math.floor(vec0.z * 32767);
-								posa[tri * 12 + 7] = Math.floor((smooth ? vec1.z : vec0.z) * 32767);
-								posa[tri * 12 + 11] = Math.floor((smooth ? vec2.z : vec0.z) * 32767);
-								nora[tri * 6    ] = Math.floor(vec0.x * 32767);
-								nora[tri * 6 + 1] = Math.floor(vec0.y * 32767);
-								nora[tri * 6 + 2] = Math.floor((smooth ? vec1.x : vec0.x) * 32767);
-								nora[tri * 6 + 3] = Math.floor((smooth ? vec1.y : vec0.y) * 32767);
-								nora[tri * 6 + 4] = Math.floor((smooth ? vec2.x : vec0.x) * 32767);
-								nora[tri * 6 + 5] = Math.floor((smooth ? vec2.y : vec0.y) * 32767);
-								if (hasuv) {
-									texa[tri * 6    ] = Math.floor(uv0[0] * 32767);
-									texa[tri * 6 + 1] = Math.floor((1.0 - uv0[1]) * 32767);
-									texa[tri * 6 + 2] = Math.floor(uv1[0] * 32767);
-									texa[tri * 6 + 3] = Math.floor((1.0 - uv1[1]) * 32767);
-									texa[tri * 6 + 4] = Math.floor(uv2[0] * 32767);
-									texa[tri * 6 + 5] = Math.floor((1.0 - uv2[1]) * 32767);
-								}
-								if (hascol) {
-									cola[tri * 9    ] = col0r * 128;
-									cola[tri * 9 + 1] = col0g * 128;
-									cola[tri * 9 + 2] = col0b * 128;
-									cola[tri * 9 + 3] = col1r * 128;
-									cola[tri * 9 + 4] = col1g * 128;
-									cola[tri * 9 + 5] = col1b * 128;
-									cola[tri * 9 + 6] = col2r * 128;
-									cola[tri * 9 + 7] = col2g * 128;
-									cola[tri * 9 + 8] = col2b * 128;
-								}
-								tri++;
-							}
-
-							for (let j = ((i + 1) % vi); j < vi - 1; ++j) { // Consume vertex
-								va[j] = va[j + 1];
-							}
-							vi--;
-							i--;
-							loops = 0;
+						for (let j = ((i + 1) % vi); j < vi - 1; ++j) { // Consume vertex
+							va[j] = va[j + 1];
 						}
+						vi--;
+						i--;
+						loops = 0;
 					}
 				}
+			}
 
-				// Apply world matrix
-				let obmat = BlHandle.get(ob, "obmat", 0, "float", 16);
-				let mat = mat4_transpose(mat4_from_f32_array(obmat));
-				let v = vec4_create();
-				for (let i = 0; i < Math.floor(posa32.length / 3); ++i) {
-					vec4_set(v, posa32[i * 3], posa32[i * 3 + 1], posa32[i * 3 + 2]);
-					vec4_apply_mat4(v, mat);
-					posa32[i * 3    ] = v.x;
-					posa32[i * 3 + 1] = v.y;
-					posa32[i * 3 + 2] = v.z;
-				}
-				mat4_get_inv(mat, mat);
-				mat4_transpose3x3(mat);
-				mat.m[12] = mat.m[13] = mat.m[14] = mat.m[15] = 0;
-				for (let i = 0; i < Math.floor(nora.length / 2); ++i) {
-					vec4_set(v, nora[i * 2] / 32767, nora[i * 2 + 1] / 32767, posa[i * 4 + 3] / 32767);
-					vec4_apply_mat(v, mat);
-					vec4_normalize(v);
-					nora[i * 2    ] = Math.floor(v.x * 32767);
-					nora[i * 2 + 1] = Math.floor(v.y * 32767);
-					posa[i * 4 + 3] = Math.floor(v.z * 32767);
-				}
+			// Apply world matrix
+			let obmat = BlHandle.get(ob, "obmat", 0, "float", 16);
+			let mat = mat4_transpose(mat4_from_f32_array(obmat));
+			let v = vec4_create();
+			for (let i = 0; i < Math.floor(posa32.length / 3); ++i) {
+				vec4_set(v, posa32[i * 3], posa32[i * 3 + 1], posa32[i * 3 + 2]);
+				vec4_apply_mat4(v, mat);
+				posa32[i * 3    ] = v.x;
+				posa32[i * 3 + 1] = v.y;
+				posa32[i * 3 + 2] = v.z;
+			}
+			mat4_get_inv(mat, mat);
+			mat4_transpose3x3(mat);
+			mat.m[12] = mat.m[13] = mat.m[14] = mat.m[15] = 0;
+			for (let i = 0; i < Math.floor(nora.length / 2); ++i) {
+				vec4_set(v, nora[i * 2] / 32767, nora[i * 2 + 1] / 32767, posa[i * 4 + 3] / 32767);
+				vec4_apply_mat(v, mat);
+				vec4_normalize(v);
+				nora[i * 2    ] = Math.floor(v.x * 32767);
+				nora[i * 2 + 1] = Math.floor(v.y * 32767);
+				posa[i * 4 + 3] = Math.floor(v.z * 32767);
+			}
 
-				// Pack positions to (-1, 1) range
-				let scalePos = 0.0;
-				for (let i = 0; i < posa32.length; ++i) {
-					let f = Math.abs(posa32[i]);
-					if (scalePos < f) scalePos = f;
-				}
-				let inv = 1 / scalePos;
-				for (let i = 0; i < Math.floor(posa32.length / 3); ++i) {
-					posa[i * 4    ] = Math.floor(posa32[i * 3    ] * 32767 * inv);
-					posa[i * 4 + 1] = Math.floor(posa32[i * 3 + 1] * 32767 * inv);
-					posa[i * 4 + 2] = Math.floor(posa32[i * 3 + 2] * 32767 * inv);
-				}
+			// Pack positions to (-1, 1) range
+			let scalePos = 0.0;
+			for (let i = 0; i < posa32.length; ++i) {
+				let f = Math.abs(posa32[i]);
+				if (scalePos < f) scalePos = f;
+			}
+			let inv = 1 / scalePos;
+			for (let i = 0; i < Math.floor(posa32.length / 3); ++i) {
+				posa[i * 4    ] = Math.floor(posa32[i * 3    ] * 32767 * inv);
+				posa[i * 4 + 1] = Math.floor(posa32[i * 3 + 1] * 32767 * inv);
+				posa[i * 4 + 2] = Math.floor(posa32[i * 3 + 2] * 32767 * inv);
+			}
 
-				let obj = {posa: posa, nora: nora, texa: texa, cola: cola, inda: inda, name: name, scalePos: scalePos, scaleTes: 1.0};
+			let obj = {posa: posa, nora: nora, texa: texa, cola: cola, inda: inda, name: name, scalePos: scalePos, scaleTes: 1.0};
 
-				(first && replaceExisting) ? ImportMesh.makeMesh(obj, path) : ImportMesh.addMesh(obj);
-				first = false;
-			}
+			(first && replaceExisting) ? ImportMesh.makeMesh(obj, path) : ImportMesh.addMesh(obj);
+			first = false;
+		}
 
-			data_delete_blob(path);
-		});
+		data_delete_blob(path);
 	}
 
 	static get_mvert_v(m: BlHandleRaw, loopstart: i32): BlHandleRaw {

+ 20 - 21
base/Sources/ImportFont.ts

@@ -10,30 +10,29 @@ class ImportFont {
 				return;
 			}
 		}
-		data_get_font(path, (font: g2_font_t) => {
-			g2_font_init(font); // Make sure font_ is ready
-			let count = krom_g2_font_count(font.font_);
-			let fontSlots: SlotFontRaw[] = [];
-			for (let i = 0; i < count; ++i) {
-				let ar = path.split(Path.sep);
-				let name = ar[ar.length - 1];
-				let f = g2_font_clone(font);
-				g2_font_set_font_index(f, i);
-				let fontSlot = SlotFont.create(name, f, path);
-				fontSlots.push(fontSlot);
-			}
+		let font: g2_font_t = data_get_font(path);
+		g2_font_init(font); // Make sure font_ is ready
+		let count = krom_g2_font_count(font.font_);
+		let fontSlots: SlotFontRaw[] = [];
+		for (let i = 0; i < count; ++i) {
+			let ar = path.split(Path.sep);
+			let name = ar[ar.length - 1];
+			let f = g2_font_clone(font);
+			g2_font_set_font_index(f, i);
+			let fontSlot = SlotFont.create(name, f, path);
+			fontSlots.push(fontSlot);
+		}
 
-			let _init = () => {
-				for (let f of fontSlots) {
-					Context.raw.font = f;
-					Project.fonts.push(f);
-					UtilRender.makeFontPreview();
-				}
+		let _init = () => {
+			for (let f of fontSlots) {
+				Context.raw.font = f;
+				Project.fonts.push(f);
+				UtilRender.makeFontPreview();
 			}
-			app_notify_on_init(_init);
+		}
+		app_notify_on_init(_init);
 
-			UIBase.hwnds[TabArea.TabStatus].redraws = 2;
-		});
+		UIBase.hwnds[TabArea.TabStatus].redraws = 2;
 	}
 }
 

+ 39 - 40
base/Sources/ImportGpl.ts

@@ -2,45 +2,44 @@
 class ImportGpl {
 
 	static run = (path: string, replaceExisting: bool) => {
-		data_get_blob(path, (b: ArrayBuffer) => {
-			// let swatches = [];
-
-			// let str = sys_buffer_to_string(b);
-			// let lines = str.split("\n");
-
-			// let view = new DataView(b);
-			// // GIMP's color palette importer: https://gitlab.gnome.org/GNOME/gimp/-/blob/gimp-2-10/app/core/gimppalette-load.c#L39
-			// if (!lines[0].startsWith("GIMP Palette")) {
-			// 	Console.error(tr("Not a valid GIMP color palette"));
-			// 	return;
-			// }
-
-			// let delimiter = ~/\s+/ig;
-			// for (let line of lines) {
-			// 	if (line.startsWith("Name:")) continue;
-			// 	else if (line.startsWith("Columns:")) continue;
-			// 	else if (line.startsWith("#")) continue;
-			// 	else {
-			// 		let tokens = delimiter.split(line);
-			// 		if (tokens.length < 3) continue;
-			// 		let swatch = Project.makeSwatch(Color.fromBytes(String(tokens[0]), String(tokens[1]), String(tokens[2])));
-			// 		swatches.push(swatch);
-			// 	}
-			// }
-
-			// if (replaceExisting) {
-			// 	Project.raw.swatches = [];
-
-			// 	if (swatches.length == 0) { // No swatches contained
-			// 		Project.raw.swatches.push(Project.makeSwatch());
-			// 	}
-			// }
-
-			// if (swatches.length > 0) {
-			// 	for (let s of swatches) {
-			// 		Project.raw.swatches.push(s);
-			// 	}
-			// }
-		});
+		let b: ArrayBuffer = data_get_blob(path);
+		// let swatches = [];
+
+		// let str = sys_buffer_to_string(b);
+		// let lines = str.split("\n");
+
+		// let view = new DataView(b);
+		// // GIMP's color palette importer: https://gitlab.gnome.org/GNOME/gimp/-/blob/gimp-2-10/app/core/gimppalette-load.c#L39
+		// if (!lines[0].startsWith("GIMP Palette")) {
+		// 	Console.error(tr("Not a valid GIMP color palette"));
+		// 	return;
+		// }
+
+		// let delimiter = ~/\s+/ig;
+		// for (let line of lines) {
+		// 	if (line.startsWith("Name:")) continue;
+		// 	else if (line.startsWith("Columns:")) continue;
+		// 	else if (line.startsWith("#")) continue;
+		// 	else {
+		// 		let tokens = delimiter.split(line);
+		// 		if (tokens.length < 3) continue;
+		// 		let swatch = Project.makeSwatch(Color.fromBytes(String(tokens[0]), String(tokens[1]), String(tokens[2])));
+		// 		swatches.push(swatch);
+		// 	}
+		// }
+
+		// if (replaceExisting) {
+		// 	Project.raw.swatches = [];
+
+		// 	if (swatches.length == 0) { // No swatches contained
+		// 		Project.raw.swatches.push(Project.makeSwatch());
+		// 	}
+		// }
+
+		// if (swatches.length > 0) {
+		// 	for (let s of swatches) {
+		// 		Project.raw.swatches.push(s);
+		// 	}
+		// }
 	}
 }

+ 65 - 67
base/Sources/ImportMesh.ts

@@ -106,58 +106,57 @@ class ImportMesh {
 		let raw = ImportMesh.rawMesh(mesh);
 		if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 });
 
-		mesh_data_create(raw, (md: mesh_data_t) => {
-			Context.raw.paintObject = Context.mainObject();
-
-			Context.selectPaintObject(Context.mainObject());
-			for (let i = 0; i < Project.paintObjects.length; ++i) {
-				let p = Project.paintObjects[i];
-				if (p == Context.raw.paintObject) continue;
-				data_delete_mesh(p.data._handle);
-				mesh_object_remove(p);
-			}
-			let handle = Context.raw.paintObject.data._handle;
-			if (handle != "SceneSphere" && handle != "ScenePlane") {
-				data_delete_mesh(handle);
-			}
+		let md: mesh_data_t = mesh_data_create(raw);
+		Context.raw.paintObject = Context.mainObject();
 
-			mesh_object_set_data(Context.raw.paintObject, md);
-			Context.raw.paintObject.base.name = mesh.name;
-			Project.paintObjects = [Context.raw.paintObject];
+		Context.selectPaintObject(Context.mainObject());
+		for (let i = 0; i < Project.paintObjects.length; ++i) {
+			let p = Project.paintObjects[i];
+			if (p == Context.raw.paintObject) continue;
+			data_delete_mesh(p.data._handle);
+			mesh_object_remove(p);
+		}
+		let handle = Context.raw.paintObject.data._handle;
+		if (handle != "SceneSphere" && handle != "ScenePlane") {
+			data_delete_mesh(handle);
+		}
 
-			md._handle = raw.name;
-			data_cached_meshes.set(md._handle, md);
+		mesh_object_set_data(Context.raw.paintObject, md);
+		Context.raw.paintObject.base.name = mesh.name;
+		Project.paintObjects = [Context.raw.paintObject];
 
-			Context.raw.ddirty = 4;
+		md._handle = raw.name;
+		data_cached_meshes.set(md._handle, md);
 
-			///if (is_paint || is_sculpt)
-			UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
-			UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
-			UtilUV.uvmapCached = false;
-			UtilUV.trianglemapCached = false;
-			UtilUV.dilatemapCached = false;
-			///end
+		Context.raw.ddirty = 4;
 
-			///if (is_paint || is_sculpt)
-			if (ImportMesh.clearLayers) {
-				while (Project.layers.length > 0) {
-					let l = Project.layers.pop();
-					SlotLayer.unload(l);
-				}
-				Base.newLayer(false);
-				app_notify_on_init(Base.initLayers);
-				History.reset();
-			}
-			///end
+		///if (is_paint || is_sculpt)
+		UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
+		UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
+		UtilUV.uvmapCached = false;
+		UtilUV.trianglemapCached = false;
+		UtilUV.dilatemapCached = false;
+		///end
 
-			// Wait for addMesh calls to finish
-			if (ImportMesh.meshesToUnwrap != null) {
-				Base.notifyOnNextFrame(ImportMesh.finishImport);
-			}
-			else {
-				app_notify_on_init(ImportMesh.finishImport);
+		///if (is_paint || is_sculpt)
+		if (ImportMesh.clearLayers) {
+			while (Project.layers.length > 0) {
+				let l = Project.layers.pop();
+				SlotLayer.unload(l);
 			}
-		});
+			Base.newLayer(false);
+			app_notify_on_init(Base.initLayers);
+			History.reset();
+		}
+		///end
+
+		// Wait for addMesh calls to finish
+		if (ImportMesh.meshesToUnwrap != null) {
+			Base.notifyOnNextFrame(ImportMesh.finishImport);
+		}
+		else {
+			app_notify_on_init(ImportMesh.finishImport);
+		}
 	}
 
 	static makeMesh = (mesh: any, path: string) => {
@@ -185,35 +184,34 @@ class ImportMesh {
 		let raw = ImportMesh.rawMesh(mesh);
 		if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 });
 
-		mesh_data_create(raw, (md: mesh_data_t) => {
+		let md: mesh_data_t = mesh_data_create(raw);
 
-			let object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
-			object.base.name = mesh.name;
-			object.skip_context = "paint";
+		let object = scene_add_mesh_object(md, Context.raw.paintObject.materials, Context.raw.paintObject.base);
+		object.base.name = mesh.name;
+		object.skip_context = "paint";
 
-			// Ensure unique names
-			for (let p of Project.paintObjects) {
-				if (p.base.name == object.base.name) {
-					p.base.name += ".001";
-					p.data._handle += ".001";
-					data_cached_meshes.set(p.data._handle, p.data);
-				}
+		// Ensure unique names
+		for (let p of Project.paintObjects) {
+			if (p.base.name == object.base.name) {
+				p.base.name += ".001";
+				p.data._handle += ".001";
+				data_cached_meshes.set(p.data._handle, p.data);
 			}
+		}
 
-			Project.paintObjects.push(object);
+		Project.paintObjects.push(object);
 
-			md._handle = raw.name;
-			data_cached_meshes.set(md._handle, md);
+		md._handle = raw.name;
+		data_cached_meshes.set(md._handle, md);
 
-			Context.raw.ddirty = 4;
+		Context.raw.ddirty = 4;
 
-			///if (is_paint || is_sculpt)
-			UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
-			UtilUV.uvmapCached = false;
-			UtilUV.trianglemapCached = false;
-			UtilUV.dilatemapCached = false;
-			///end
-		});
+		///if (is_paint || is_sculpt)
+		UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
+		UtilUV.uvmapCached = false;
+		UtilUV.trianglemapCached = false;
+		UtilUV.dilatemapCached = false;
+		///end
 	}
 
 	static addMesh = (mesh: any) => {

+ 96 - 97
base/Sources/ImportObj.ts

@@ -9,110 +9,109 @@ class ImportObj {
 			 i == SplitType.SplitGroup 			   ? "g".charCodeAt(0) :
 			 				 			   			 "u".charCodeAt(0); // usemtl
 
-		data_get_blob(path, (b: ArrayBuffer) => {
+		let b: ArrayBuffer = data_get_blob(path);
 
-			if (isUdim) {
-				let part = krom_io_obj_parse(b, splitCode, 0, isUdim);
-				let name = part.name;
-				for (let i = 0; i < part.udims.length; ++i) {
-					if (part.udims[i].length == 0) continue;
-					let u = i % part.udims_u;
-					let v = Math.floor(i / part.udims_u);
-					part.name = name + "." + (1000 + v * 10 + u + 1);
-					part.inda = part.udims[i];
-					i == 0 ? (replaceExisting ? ImportMesh.makeMesh(part, path) : ImportMesh.addMesh(part)) : ImportMesh.addMesh(part);
-				}
+		if (isUdim) {
+			let part = krom_io_obj_parse(b, splitCode, 0, isUdim);
+			let name = part.name;
+			for (let i = 0; i < part.udims.length; ++i) {
+				if (part.udims[i].length == 0) continue;
+				let u = i % part.udims_u;
+				let v = Math.floor(i / part.udims_u);
+				part.name = name + "." + (1000 + v * 10 + u + 1);
+				part.inda = part.udims[i];
+				i == 0 ? (replaceExisting ? ImportMesh.makeMesh(part, path) : ImportMesh.addMesh(part)) : ImportMesh.addMesh(part);
 			}
-			else {
-				let parts: any[] = [];
-				let part = krom_io_obj_parse(b, splitCode, 0, false);
-				parts.push(part);
-				while (part.has_next) {
-					part = krom_io_obj_parse(b, splitCode, part.pos, false);
-					// This part does not contain faces (may contain lines only)
-					if (part.inda.length == 0) {
-						continue;
-					}
-					parts.push(part);
+		}
+		else {
+			let parts: any[] = [];
+			let part = krom_io_obj_parse(b, splitCode, 0, false);
+			parts.push(part);
+			while (part.has_next) {
+				part = krom_io_obj_parse(b, splitCode, part.pos, false);
+				// This part does not contain faces (may contain lines only)
+				if (part.inda.length == 0) {
+					continue;
 				}
-				if (Context.raw.splitBy == SplitType.SplitMaterial) {
-					let posa0;
-					let posa1;
-					let nora0;
-					let nora1;
-					let texa0;
-					let texa1;
-					let inda0;
-					let inda1;
-					// Merge to single object per material
-					for (let i = 0; i < parts.length; ++i) {
-						let j = i + 1;
-						while (j < parts.length) {
-							if (parts[i].name == parts[j].name) {
-								posa0 = parts[i].posa;
-								posa1 = parts[j].posa;
-								nora0 = parts[i].nora;
-								nora1 = parts[j].nora;
-								texa0 = parts[i].texa != null ? parts[i].texa : null;
-								texa1 = parts[j].texa != null ? parts[j].texa : null;
-								inda0 = parts[i].inda;
-								inda1 = parts[j].inda;
-								let voff = Math.floor(posa0.length / 4);
-								// Repack merged positions
-								let posa32 = new Float32Array(Math.floor(posa0.length / 4) * 3 + Math.floor(posa1.length / 4) * 3);
-								for (let k = 0; k < Math.floor(posa0.length / 4); ++k) {
-									posa32[k * 3    ] = posa0[k * 4    ] / 32767 * parts[i].scalePos;
-									posa32[k * 3 + 1] = posa0[k * 4 + 1] / 32767 * parts[i].scalePos;
-									posa32[k * 3 + 2] = posa0[k * 4 + 2] / 32767 * parts[i].scalePos;
-								}
-								for (let k = 0; k < Math.floor(posa1.length / 4); ++k) {
-									posa32[voff * 3 + k * 3    ] = posa1[k * 4    ] / 32767 * parts[j].scalePos;
-									posa32[voff * 3 + k * 3 + 1] = posa1[k * 4 + 1] / 32767 * parts[j].scalePos;
-									posa32[voff * 3 + k * 3 + 2] = posa1[k * 4 + 2] / 32767 * parts[j].scalePos;
-								}
-								let scalePos = 0.0;
-								for (let k = 0; k < posa32.length; ++k) {
-									let f = Math.abs(posa32[k]);
-									if (scalePos < f) scalePos = f;
-								}
-								let inv = 32767 * (1 / scalePos);
-								let posa = new Int16Array(posa0.length + posa1.length);
-								for (let k = 0; k < Math.floor(posa.length / 4); ++k) {
-									posa[k * 4    ] = Math.floor(posa32[k * 3    ] * inv);
-									posa[k * 4 + 1] = Math.floor(posa32[k * 3 + 1] * inv);
-									posa[k * 4 + 2] = Math.floor(posa32[k * 3 + 2] * inv);
-								}
-								for (let k = 0; k < Math.floor(posa0.length / 4); ++k) posa[k * 4 + 3] = posa0[k * 4 + 3];
-								for (let k = 0; k < Math.floor(posa1.length / 4); ++k) posa[posa0.length + k * 4 + 3] = posa1[k * 4 + 3];
-								// Merge normals and uvs
-								let nora = new Int16Array(nora0.length + nora1.length);
-								let texa = (texa0 != null && texa1 != null) ? new Int16Array(texa0.length + texa1.length) : null;
-								let inda = new Uint32Array(inda0.length + inda1.length);
-								nora.set(nora0);
-								nora.set(nora1, nora0.length);
-								if (texa != null) {
-									texa.set(texa0);
-									texa.set(texa1, texa0.length);
-								}
-								inda.set(inda0);
-								for (let k = 0; k < inda1.length; ++k) inda[k + inda0.length] = inda1[k] + voff;
-								parts[i].posa = posa;
-								parts[i].nora = nora;
-								parts[i].texa = texa;
-								parts[i].inda = inda;
-								parts[i].scalePos = scalePos;
-								parts.splice(j, 1);
+				parts.push(part);
+			}
+			if (Context.raw.splitBy == SplitType.SplitMaterial) {
+				let posa0;
+				let posa1;
+				let nora0;
+				let nora1;
+				let texa0;
+				let texa1;
+				let inda0;
+				let inda1;
+				// Merge to single object per material
+				for (let i = 0; i < parts.length; ++i) {
+					let j = i + 1;
+					while (j < parts.length) {
+						if (parts[i].name == parts[j].name) {
+							posa0 = parts[i].posa;
+							posa1 = parts[j].posa;
+							nora0 = parts[i].nora;
+							nora1 = parts[j].nora;
+							texa0 = parts[i].texa != null ? parts[i].texa : null;
+							texa1 = parts[j].texa != null ? parts[j].texa : null;
+							inda0 = parts[i].inda;
+							inda1 = parts[j].inda;
+							let voff = Math.floor(posa0.length / 4);
+							// Repack merged positions
+							let posa32 = new Float32Array(Math.floor(posa0.length / 4) * 3 + Math.floor(posa1.length / 4) * 3);
+							for (let k = 0; k < Math.floor(posa0.length / 4); ++k) {
+								posa32[k * 3    ] = posa0[k * 4    ] / 32767 * parts[i].scalePos;
+								posa32[k * 3 + 1] = posa0[k * 4 + 1] / 32767 * parts[i].scalePos;
+								posa32[k * 3 + 2] = posa0[k * 4 + 2] / 32767 * parts[i].scalePos;
+							}
+							for (let k = 0; k < Math.floor(posa1.length / 4); ++k) {
+								posa32[voff * 3 + k * 3    ] = posa1[k * 4    ] / 32767 * parts[j].scalePos;
+								posa32[voff * 3 + k * 3 + 1] = posa1[k * 4 + 1] / 32767 * parts[j].scalePos;
+								posa32[voff * 3 + k * 3 + 2] = posa1[k * 4 + 2] / 32767 * parts[j].scalePos;
 							}
-							else j++;
+							let scalePos = 0.0;
+							for (let k = 0; k < posa32.length; ++k) {
+								let f = Math.abs(posa32[k]);
+								if (scalePos < f) scalePos = f;
+							}
+							let inv = 32767 * (1 / scalePos);
+							let posa = new Int16Array(posa0.length + posa1.length);
+							for (let k = 0; k < Math.floor(posa.length / 4); ++k) {
+								posa[k * 4    ] = Math.floor(posa32[k * 3    ] * inv);
+								posa[k * 4 + 1] = Math.floor(posa32[k * 3 + 1] * inv);
+								posa[k * 4 + 2] = Math.floor(posa32[k * 3 + 2] * inv);
+							}
+							for (let k = 0; k < Math.floor(posa0.length / 4); ++k) posa[k * 4 + 3] = posa0[k * 4 + 3];
+							for (let k = 0; k < Math.floor(posa1.length / 4); ++k) posa[posa0.length + k * 4 + 3] = posa1[k * 4 + 3];
+							// Merge normals and uvs
+							let nora = new Int16Array(nora0.length + nora1.length);
+							let texa = (texa0 != null && texa1 != null) ? new Int16Array(texa0.length + texa1.length) : null;
+							let inda = new Uint32Array(inda0.length + inda1.length);
+							nora.set(nora0);
+							nora.set(nora1, nora0.length);
+							if (texa != null) {
+								texa.set(texa0);
+								texa.set(texa1, texa0.length);
+							}
+							inda.set(inda0);
+							for (let k = 0; k < inda1.length; ++k) inda[k + inda0.length] = inda1[k] + voff;
+							parts[i].posa = posa;
+							parts[i].nora = nora;
+							parts[i].texa = texa;
+							parts[i].inda = inda;
+							parts[i].scalePos = scalePos;
+							parts.splice(j, 1);
 						}
+						else j++;
 					}
 				}
-				replaceExisting ? ImportMesh.makeMesh(parts[0], path) : ImportMesh.addMesh(parts[0]);
-				for (let i = 1; i < parts.length; ++i) {
-					ImportMesh.addMesh(parts[i]);
-				}
 			}
-			data_delete_blob(path);
-		});
+			replaceExisting ? ImportMesh.makeMesh(parts[0], path) : ImportMesh.addMesh(parts[0]);
+			for (let i = 1; i < parts.length; ++i) {
+				ImportMesh.addMesh(parts[i]);
+			}
+		}
+		data_delete_blob(path);
 	}
 }

+ 5 - 5
base/Sources/ImportTexture.ts

@@ -14,10 +14,9 @@ class ImportTexture {
 			if (a.file == path) {
 				// Set as envmap
 				if (hdrAsEnvmap && path.toLowerCase().endsWith(".hdr")) {
-					data_get_image(path, (image: image_t) => {
-						Base.notifyOnNextFrame(() => { // Make sure file browser process did finish
-							ImportEnvmap.run(path, image);
-						});
+					let image: image_t = data_get_image(path);
+					Base.notifyOnNextFrame(() => { // Make sure file browser process did finish
+						ImportEnvmap.run(path, image);
 					});
 				}
 				Console.info(Strings.info0());
@@ -52,6 +51,7 @@ class ImportTexture {
 	}
 
 	static defaultImporter = (path: string, done: (img: image_t)=>void) => {
-		data_get_image(path, done);
+		let img: image_t = data_get_image(path);
+		done(img);
 	}
 }

+ 1 - 1
base/Sources/LineDraw.ts

@@ -1,7 +1,7 @@
 
 class LineDraw {
 
-	static color: Color = 0xffff0000;
+	static color: color_t = 0xffff0000;
 	static strength = 0.005;
 	static mat: mat4_t = null;
 	static dim: vec4_t = null;

+ 5 - 6
base/Sources/Plugin.ts

@@ -23,12 +23,11 @@ class Plugin {
 
 	static start = (plugin: string) => {
 		try {
-			data_get_blob("plugins/" + plugin, (blob: ArrayBuffer) => {
-				Plugin.pluginName = plugin;
-				// (1, eval)(sys_buffer_to_string(blob)); // Global scope
-				eval(sys_buffer_to_string(blob)); // Local scope
-				data_delete_blob("plugins/" + plugin);
-			});
+			let blob: ArrayBuffer = data_get_blob("plugins/" + plugin);
+			Plugin.pluginName = 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 + "'");

+ 82 - 86
base/Sources/Project.ts

@@ -189,13 +189,11 @@ class Project {
 				///end
 			}
 			else {
-				data_get_blob("meshes/" + Project.meshList[Context.raw.projectType] + ".arm", (b: ArrayBuffer) => {
-					raw = armpack_decode(b).mesh_datas[0];
-				});
+				let b: ArrayBuffer = data_get_blob("meshes/" + Project.meshList[Context.raw.projectType] + ".arm");
+				raw = armpack_decode(b).mesh_datas[0];
 			}
 
-			let md: mesh_data_t;
-			mesh_data_create(raw, (mdata: mesh_data_t) => { md = mdata; });
+			let md: mesh_data_t = mesh_data_create(raw);
 			data_cached_meshes.set("SceneTessellated", md);
 
 			if (Context.raw.projectType == ProjectModel.ModelTessellatedPlane) {
@@ -204,106 +202,104 @@ class Project {
 		}
 
 		let n = Context.raw.projectType == ProjectModel.ModelRoundedCube ? ".Cube" : "Tessellated";
-		data_get_mesh("Scene", n, (md: mesh_data_t) => {
-
-			let current = _g2_current;
-			if (current != null) g2_end();
+		let md: mesh_data_t = data_get_mesh("Scene", n);
 
-			///if is_paint
-			Context.raw.pickerMaskHandle.position = PickerMask.MaskNone;
-			///end
+		let current = _g2_current;
+		if (current != null) g2_end();
 
-			mesh_object_set_data(Context.raw.paintObject, md);
-			vec4_set(Context.raw.paintObject.base.transform.scale, 1, 1, 1);
-			transform_build_matrix(Context.raw.paintObject.base.transform);
-			Context.raw.paintObject.base.name = n;
-			Project.paintObjects = [Context.raw.paintObject];
-			///if (is_paint || is_sculpt)
-			while (Project.materials.length > 0) SlotMaterial.unload(Project.materials.pop());
-			///end
-			data_get_material("Scene", "Material", (m: material_data_t) => {
-				///if (is_paint || is_sculpt)
-				Project.materials.push(SlotMaterial.create(m));
-				///end
-				///if is_lab
-				Project.materialData = m;
-				///end
-			});
+		///if is_paint
+		Context.raw.pickerMaskHandle.position = PickerMask.MaskNone;
+		///end
 
-			///if (is_paint || is_sculpt)
-			Context.raw.material = Project.materials[0];
-			///end
+		mesh_object_set_data(Context.raw.paintObject, md);
+		vec4_set(Context.raw.paintObject.base.transform.scale, 1, 1, 1);
+		transform_build_matrix(Context.raw.paintObject.base.transform);
+		Context.raw.paintObject.base.name = n;
+		Project.paintObjects = [Context.raw.paintObject];
+		///if (is_paint || is_sculpt)
+		while (Project.materials.length > 0) SlotMaterial.unload(Project.materials.pop());
+		///end
+		let m: material_data_t = data_get_material("Scene", "Material");
+		///if (is_paint || is_sculpt)
+		Project.materials.push(SlotMaterial.create(m));
+		///end
+		///if is_lab
+		Project.materialData = m;
+		///end
 
-			UINodes.hwnd.redraws = 2;
-			UINodes.groupStack = [];
-			Project.materialGroups = [];
+		///if (is_paint || is_sculpt)
+		Context.raw.material = Project.materials[0];
+		///end
 
-			///if (is_paint || is_sculpt)
-			Project.brushes = [SlotBrush.create()];
-			Context.raw.brush = Project.brushes[0];
+		UINodes.hwnd.redraws = 2;
+		UINodes.groupStack = [];
+		Project.materialGroups = [];
 
-			Project.fonts = [SlotFont.create("default.ttf", Base.font)];
-			Context.raw.font = Project.fonts[0];
-			///end
+		///if (is_paint || is_sculpt)
+		Project.brushes = [SlotBrush.create()];
+		Context.raw.brush = Project.brushes[0];
 
-			Project.setDefaultSwatches();
-			Context.raw.swatch = Project.raw.swatches[0];
+		Project.fonts = [SlotFont.create("default.ttf", Base.font)];
+		Context.raw.font = Project.fonts[0];
+		///end
 
-			Context.raw.pickedColor = Project.makeSwatch();
-			Context.raw.colorPickerCallback = null;
-			History.reset();
+		Project.setDefaultSwatches();
+		Context.raw.swatch = Project.raw.swatches[0];
 
-			MakeMaterial.parsePaintMaterial();
+		Context.raw.pickedColor = Project.makeSwatch();
+		Context.raw.colorPickerCallback = null;
+		History.reset();
 
-			///if (is_paint || is_sculpt)
-			UtilRender.makeMaterialPreview();
-			///end
+		MakeMaterial.parsePaintMaterial();
 
-			for (let a of Project.assets) data_delete_image(a.file);
-			Project.assets = [];
-			Project.assetNames = [];
-			Project.assetMap = new Map();
-			Project.assetId = 0;
-			Project.raw.packed_assets = [];
-			Context.raw.ddirty = 4;
+		///if (is_paint || is_sculpt)
+		UtilRender.makeMaterialPreview();
+		///end
 
-			///if (is_paint || is_sculpt)
-			UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
-			UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
-			///end
+		for (let a of Project.assets) data_delete_image(a.file);
+		Project.assets = [];
+		Project.assetNames = [];
+		Project.assetMap = new Map();
+		Project.assetId = 0;
+		Project.raw.packed_assets = [];
+		Context.raw.ddirty = 4;
 
-			if (resetLayers) {
+		///if (is_paint || is_sculpt)
+		UIBase.hwnds[TabArea.TabSidebar0].redraws = 2;
+		UIBase.hwnds[TabArea.TabSidebar1].redraws = 2;
+		///end
 
-				///if (is_paint || is_sculpt)
-				let aspectRatioChanged = Project.layers[0].texpaint.width != Config.getTextureResX() || Project.layers[0].texpaint.height != Config.getTextureResY();
-				while (Project.layers.length > 0) SlotLayer.unload(Project.layers.pop());
-				let layer = SlotLayer.create();
-				Project.layers.push(layer);
-				Context.setLayer(layer);
-				if (aspectRatioChanged) {
-					app_notify_on_init(Base.resizeLayers);
-				}
-				///end
+		if (resetLayers) {
 
-				app_notify_on_init(Base.initLayers);
+			///if (is_paint || is_sculpt)
+			let aspectRatioChanged = Project.layers[0].texpaint.width != Config.getTextureResX() || Project.layers[0].texpaint.height != Config.getTextureResY();
+			while (Project.layers.length > 0) SlotLayer.unload(Project.layers.pop());
+			let layer = SlotLayer.create();
+			Project.layers.push(layer);
+			Context.setLayer(layer);
+			if (aspectRatioChanged) {
+				app_notify_on_init(Base.resizeLayers);
 			}
+			///end
 
-			if (current != null) g2_begin(current, false);
+			app_notify_on_init(Base.initLayers);
+		}
 
-			Context.raw.savedEnvmap = null;
-			Context.raw.envmapLoaded = false;
-			scene_world._envmap = Context.raw.emptyEnvmap;
-			scene_world.envmap = "World_radiance.k";
-			Context.raw.showEnvmapHandle.selected = Context.raw.showEnvmap = false;
-			scene_world._radiance = Context.raw.defaultRadiance;
-			scene_world._radiance_mipmaps = Context.raw.defaultRadianceMipmaps;
-			scene_world._irradiance = Context.raw.defaultIrradiance;
-			scene_world.strength = 4.0;
+		if (current != null) g2_begin(current, false);
 
-			///if (is_paint || is_sculpt)
-			Context.initTool();
-			///end
-		});
+		Context.raw.savedEnvmap = null;
+		Context.raw.envmapLoaded = false;
+		scene_world._envmap = Context.raw.emptyEnvmap;
+		scene_world.envmap = "World_radiance.k";
+		Context.raw.showEnvmapHandle.selected = Context.raw.showEnvmap = false;
+		scene_world._radiance = Context.raw.defaultRadiance;
+		scene_world._radiance_mipmaps = Context.raw.defaultRadianceMipmaps;
+		scene_world._irradiance = Context.raw.defaultIrradiance;
+		scene_world.strength = 4.0;
+
+		///if (is_paint || is_sculpt)
+		Context.initTool();
+		///end
 
 		///if (krom_direct3d12 || krom_vulkan || krom_metal)
 		RenderPathRaytrace.ready = false;

+ 2 - 2
base/Sources/ProjectFormat.ts

@@ -49,12 +49,12 @@ type TPackedAsset = {
 }
 
 type TSwatchColor = {
-	base: Color;
+	base: color_t;
 	opacity: f32;
 	occlusion: f32;
 	roughness: f32;
 	metallic: f32;
-	normal: Color;
+	normal: color_t;
 	emission: f32;
 	height: f32;
 	subsurface: f32;

+ 9 - 10
base/Sources/RenderPathRaytrace.ts

@@ -137,18 +137,17 @@ class RenderPathRaytrace {
 	static raytraceInit = (shaderName: string, build = true) => {
 		if (RenderPathRaytrace.first) {
 			RenderPathRaytrace.first = false;
-			scene_embed_data("bnoise_sobol.k", () => {});
-			scene_embed_data("bnoise_scramble.k", () => {});
-			scene_embed_data("bnoise_rank.k", () => {});
+			scene_embed_data("bnoise_sobol.k");
+			scene_embed_data("bnoise_scramble.k");
+			scene_embed_data("bnoise_rank.k");
 		}
 
-		data_get_blob(shaderName, (shader: ArrayBuffer) => {
-			if (build) RenderPathRaytrace.buildData();
-			let bnoise_sobol = scene_embedded.get("bnoise_sobol.k");
-			let bnoise_scramble = scene_embedded.get("bnoise_scramble.k");
-			let bnoise_rank = scene_embedded.get("bnoise_rank.k");
-			krom_raytrace_init(shader, RenderPathRaytrace.vb.buffer_, RenderPathRaytrace.ib.buffer_, RenderPathRaytrace.vb_scale);
-		});
+		let shader: ArrayBuffer = data_get_blob(shaderName);
+		if (build) RenderPathRaytrace.buildData();
+		let bnoise_sobol = scene_embedded.get("bnoise_sobol.k");
+		let bnoise_scramble = scene_embedded.get("bnoise_scramble.k");
+		let bnoise_rank = scene_embedded.get("bnoise_rank.k");
+		krom_raytrace_init(shader, RenderPathRaytrace.vb.buffer_, RenderPathRaytrace.ib.buffer_, RenderPathRaytrace.vb_scale);
 	}
 
 	static buildData = () => {

+ 4 - 5
base/Sources/Res.ts

@@ -6,11 +6,10 @@ class Res {
 	static load = (names: string[], done: ()=>void) => {
 		let loaded = 0;
 		for (let s of names) {
-			data_get_image(s, (image: image_t) => {
-				Res.bundled.set(s, image);
-				loaded++;
-				if (loaded == names.length) done();
-			});
+			let image: image_t = data_get_image(s);
+			Res.bundled.set(s, image);
+			loaded++;
+			if (loaded == names.length) done();
 		}
 	}
 

+ 2 - 1
base/Sources/TabConsole.ts

@@ -51,7 +51,8 @@ class TabConsole {
 
 			let _font = ui.font;
 			let _fontSize = ui.font_size;
-			data_get_font("font_mono.ttf", (f: g2_font_t) => { zui_set_font(ui, f); }); // Sync
+			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.lastTraces) {
 				zui_text(t);

+ 1 - 2
base/Sources/TabMeshes.ts

@@ -162,8 +162,7 @@ class TabMeshes {
 				scale_pos: mesh.scalePos,
 				scale_tex: mesh.scaleTex
 			};
-			let md: mesh_data_t;
-			mesh_data_create(raw, (_md: mesh_data_t) => { md = _md; });
+			let md: mesh_data_t = mesh_data_create(raw);
 			mo = mesh_object_create(md, Context.raw.paintObject.materials);
 			array_remove(scene_meshes, mo);
 			mo.base.name = "Tessellated";

+ 11 - 10
base/Sources/TabScript.ts

@@ -29,10 +29,9 @@ class TabScript {
 			}
 			if (zui_button(tr("Import"))) {
 				UIFiles.show("js", false, false, (path: string) => {
-					data_get_blob(path, (b: ArrayBuffer) => {
-						TabScript.hscript.text = sys_buffer_to_string(b);
-						data_delete_blob(path);
-					});
+					let b: ArrayBuffer = data_get_blob(path);
+					TabScript.hscript.text = sys_buffer_to_string(b);
+					data_delete_blob(path);
 				});
 			}
 			if (zui_button(tr("Export"))) {
@@ -49,7 +48,8 @@ class TabScript {
 
 			let _font = ui.font;
 			let _fontSize = ui.font_size;
-			data_get_font("font_mono.ttf", (f: g2_font_t) => { zui_set_font(ui, f); }); // Sync
+			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));
 			zui_set_text_area_line_numbers(true);
 			zui_set_text_area_scroll_past_end(true);
@@ -65,11 +65,12 @@ class TabScript {
 
 	static getTextColoring = (): zui_text_coloring_t => {
 		if (TabScript.textColoring == null) {
-			data_get_blob("text_coloring.json", (blob: ArrayBuffer) => {
-				TabScript.textColoring = JSON.parse(sys_buffer_to_string(blob));
-				TabScript.textColoring.default_color = Math.floor(TabScript.textColoring.default_color);
-				for (let coloring of TabScript.textColoring.colorings) coloring.color = Math.floor(coloring.color);
-			});
+			let blob: ArrayBuffer = data_get_blob("text_coloring.json");
+			TabScript.textColoring = JSON.parse(sys_buffer_to_string(blob));
+			TabScript.textColoring.default_color = Math.floor(TabScript.textColoring.default_color);
+			for (let coloring of TabScript.textColoring.colorings) {
+				coloring.color = Math.floor(coloring.color);
+			}
 		}
 		return TabScript.textColoring;
 	}

+ 15 - 16
base/Sources/Translator.ts

@@ -114,22 +114,21 @@ class Translator {
 		_g2_font_glyphs.sort((a: i32, b: i32) => { return a - b; });
 		// Load and assign font with cjk characters
 		app_notify_on_init(() => {
-			data_get_font(fontPath, (f: g2_font_t) => {
-				if (cjk) {
-					let acjkFontIndices = Translator.cjkFontIndices as any;
-					let fontIndex = Translator.cjkFontIndices.has(Config.raw.locale) ? acjkFontIndices[Config.raw.locale] : 0;
-					g2_font_set_font_index(f, fontIndex);
-				}
-				Base.font = f;
-				// Scale up the font size and elements width a bit
-				Base.theme.FONT_SIZE = Math.floor(Base.defaultFontSize * fontScale);
-				Base.theme.ELEMENT_W = Math.floor(Base.defaultElementW * (Config.raw.locale != "en" ? 1.4 : 1.0));
-				let uis = Base.getUIs();
-				for (let ui of uis) {
-					zui_set_font(ui, f);
-					zui_set_scale(ui, zui_SCALE(ui));
-				}
-			});
+			let f: g2_font_t = data_get_font(fontPath);
+			if (cjk) {
+				let acjkFontIndices = Translator.cjkFontIndices as any;
+				let fontIndex = Translator.cjkFontIndices.has(Config.raw.locale) ? acjkFontIndices[Config.raw.locale] : 0;
+				g2_font_set_font_index(f, fontIndex);
+			}
+			Base.font = f;
+			// Scale up the font size and elements width a bit
+			Base.theme.FONT_SIZE = Math.floor(Base.defaultFontSize * fontScale);
+			Base.theme.ELEMENT_W = Math.floor(Base.defaultElementW * (Config.raw.locale != "en" ? 1.4 : 1.0));
+			let uis = Base.getUIs();
+			for (let ui of uis) {
+				zui_set_font(ui, f);
+				zui_set_scale(ui, zui_SCALE(ui));
+			}
 		});
 	}
 

+ 33 - 38
base/Sources/UIBase.ts

@@ -121,10 +121,9 @@ class UIBase {
 		///if (is_paint || is_sculpt)
 		if (Project.materials == null) {
 			Project.materials = [];
-			data_get_material("Scene", "Material", (m: material_data_t) => {
-				Project.materials.push(SlotMaterial.create(m));
-				Context.raw.material = Project.materials[0];
-			});
+			let m: material_data_t = data_get_material("Scene", "Material");
+			Project.materials.push(SlotMaterial.create(m));
+			Context.raw.material = Project.materials[0];
 		}
 
 		if (Project.brushes == null) {
@@ -149,15 +148,13 @@ class UIBase {
 
 		///if is_lab
 		if (Project.materialData == null) {
-			data_get_material("Scene", "Material", (m: material_data_t) => {
-				Project.materialData = m;
-			});
+			let m: material_data_t = data_get_material("Scene", "Material");
+			Project.materialData = m;
 		}
 
 		if (Project.defaultCanvas == null) { // Synchronous
-			data_get_blob("default_brush.arm", (b: ArrayBuffer) => {
-				Project.defaultCanvas = b;
-			});
+			let b: ArrayBuffer = data_get_blob("default_brush.arm");
+			Project.defaultCanvas = b;
 		}
 
 		Project.nodes = zui_nodes_create();
@@ -710,34 +707,32 @@ class UIBase {
 				}
 				History.pushUndo = true;
 				Context.raw.particleHitX = Context.raw.particleHitY = Context.raw.particleHitZ = 0;
-				scene_spawn_object(".Sphere", null, (o: object_t) => {
-					data_get_material("Scene", ".Gizmo", (md: material_data_t) => {
-						let mo: mesh_object_t = o.ext;
-						mo.base.name = ".Bullet";
-						mo.materials[0] = md;
-						mo.base.visible = true;
-
-						let camera = scene_camera;
-						let ct = camera.base.transform;
-						vec4_set(mo.base.transform.loc, transform_world_x(ct), transform_world_y(ct), transform_world_z(ct));
-						vec4_set(mo.base.transform.scale, Context.raw.brushRadius * 0.2, Context.raw.brushRadius * 0.2, Context.raw.brushRadius * 0.2);
-						transform_build_matrix(mo.base.transform);
-
-						let body = PhysicsBody.create();
-						body.shape = ShapeType.ShapeSphere;
-						body.mass = 1.0;
-						body.ccd = true;
-						mo.base.transform.radius /= 10; // Lower ccd radius
-						PhysicsBody.init(body, mo.base);
-						(mo.base as any).physicsBody = body;
-						mo.base.transform.radius *= 10;
-
-						let ray = raycast_get_ray(mouse_view_x(), mouse_view_y(), camera);
-						PhysicsBody.applyImpulse(body, vec4_mult(ray.dir, 0.15));
-
-						Context.raw.particleTimer = tween_timer(5, function() { mesh_object_remove(mo); });
-					});
-				});
+				let o: object_t = scene_spawn_object(".Sphere");
+				let md: material_data_t = data_get_material("Scene", ".Gizmo");
+				let mo: mesh_object_t = o.ext;
+				mo.base.name = ".Bullet";
+				mo.materials[0] = md;
+				mo.base.visible = true;
+
+				let camera = scene_camera;
+				let ct = camera.base.transform;
+				vec4_set(mo.base.transform.loc, transform_world_x(ct), transform_world_y(ct), transform_world_z(ct));
+				vec4_set(mo.base.transform.scale, Context.raw.brushRadius * 0.2, Context.raw.brushRadius * 0.2, Context.raw.brushRadius * 0.2);
+				transform_build_matrix(mo.base.transform);
+
+				let body = PhysicsBody.create();
+				body.shape = ShapeType.ShapeSphere;
+				body.mass = 1.0;
+				body.ccd = true;
+				mo.base.transform.radius /= 10; // Lower ccd radius
+				PhysicsBody.init(body, mo.base);
+				(mo.base as any).physicsBody = body;
+				mo.base.transform.radius *= 10;
+
+				let ray = raycast_get_ray(mouse_view_x(), mouse_view_y(), camera);
+				PhysicsBody.applyImpulse(body, vec4_mult(ray.dir, 0.15));
+
+				Context.raw.particleTimer = tween_timer(5, function() { mesh_object_remove(mo); });
 			}
 
 			let pairs = PhysicsWorld.getContactPairs(world, Context.raw.paintBody);

+ 34 - 36
base/Sources/UIFiles.ts

@@ -171,27 +171,26 @@ class UIFiles {
 							UIFiles.iconMap.set(handle.text + Path.sep + f, empty);
 							File.cacheCloud(handle.text + Path.sep + iconFile, (abs: string) => {
 								if (abs != null) {
-									data_get_image(abs, (image: image_t) => {
-										app_notify_on_init(() => {
-											if (Base.pipeCopyRGB == null) Base.makePipeCopyRGB();
-											icon = image_create_render_target(image.width, image.height);
-											if (f.endsWith(".arm")) { // Used for material sphere alpha cutout
-												g2_begin(icon, false);
-
-												///if (is_paint || is_sculpt)
-												g2_draw_image(Project.materials[0].image, 0, 0);
-												///end
-											}
-											else {
-												g2_begin(icon, true, 0xffffffff);
-											}
-											g2_set_pipeline(Base.pipeCopyRGB);
-											g2_draw_image(image, 0, 0);
-											g2_set_pipeline(null);
-											g2_end();
-											UIFiles.iconMap.set(handle.text + Path.sep + f, icon);
-											UIBase.hwnds[TabArea.TabStatus].redraws = 3;
-										});
+									let image: image_t = data_get_image(abs);
+									app_notify_on_init(() => {
+										if (Base.pipeCopyRGB == null) Base.makePipeCopyRGB();
+										icon = image_create_render_target(image.width, image.height);
+										if (f.endsWith(".arm")) { // Used for material sphere alpha cutout
+											g2_begin(icon, false);
+
+											///if (is_paint || is_sculpt)
+											g2_draw_image(Project.materials[0].image, 0, 0);
+											///end
+										}
+										else {
+											g2_begin(icon, true, 0xffffffff);
+										}
+										g2_set_pipeline(Base.pipeCopyRGB);
+										g2_draw_image(image, 0, 0);
+										g2_set_pipeline(null);
+										g2_end();
+										UIFiles.iconMap.set(handle.text + Path.sep + f, icon);
+										UIBase.hwnds[TabArea.TabStatus].redraws = 3;
 									});
 								}
 								else UIFiles.offline = true;
@@ -279,21 +278,20 @@ class UIFiles {
 					if (icon == null) {
 						let empty = render_path_render_targets.get("empty_black").image;
 						UIFiles.iconMap.set(shandle, empty);
-						data_get_image(shandle, (image: image_t) => {
-							app_notify_on_init(() => {
-								if (Base.pipeCopyRGB == null) Base.makePipeCopyRGB();
-								let sw = image.width > image.height ? w : Math.floor(1.0 * image.width / image.height * w);
-								let sh = image.width > image.height ? Math.floor(1.0 * image.height / image.width * w) : w;
-								icon = image_create_render_target(sw, sh);
-								g2_begin(icon, true, 0xffffffff);
-								g2_set_pipeline(Base.pipeCopyRGB);
-								g2_draw_scaled_image(image, 0, 0, sw, sh);
-								g2_set_pipeline(null);
-								g2_end();
-								UIFiles.iconMap.set(shandle, icon);
-								UIBase.hwnds[TabArea.TabStatus].redraws = 3;
-								data_delete_image(shandle); // The big image is not needed anymore
-							});
+						let image: image_t = data_get_image(shandle);
+						app_notify_on_init(() => {
+							if (Base.pipeCopyRGB == null) Base.makePipeCopyRGB();
+							let sw = image.width > image.height ? w : Math.floor(1.0 * image.width / image.height * w);
+							let sh = image.width > image.height ? Math.floor(1.0 * image.height / image.width * w) : w;
+							icon = image_create_render_target(sw, sh);
+							g2_begin(icon, true, 0xffffffff);
+							g2_set_pipeline(Base.pipeCopyRGB);
+							g2_draw_scaled_image(image, 0, 0, sw, sh);
+							g2_set_pipeline(null);
+							g2_end();
+							UIFiles.iconMap.set(shandle, icon);
+							UIBase.hwnds[TabArea.TabStatus].redraws = 3;
+							data_delete_image(shandle); // The big image is not needed anymore
 						});
 					}
 					if (icon != null) {

+ 1 - 1
base/Sources/UIHeader.ts

@@ -114,7 +114,7 @@ class UIHeader {
 			let opacityPicked = Math.round(Context.raw.pickedColor.opacity * 100) / 100;
 
 			let h = zui_handle("uiheader_0");
-			let color: Color = 0xffffffff;
+			let color: color_t = 0xffffffff;
 			color = color_set_rb(color, baseRPicked * 255);
 			color = color_set_gb(color, baseGPicked * 255);
 			color = color_set_bb(color, baseBPicked * 255);

+ 3 - 4
base/Sources/UIMenu.ts

@@ -484,10 +484,9 @@ class UIMenu {
 						let tabVertical = Config.raw.touch_ui;
 						if (zui_tab(zui_handle("uimenu_13"), tr("About"), tabVertical)) {
 
-							data_get_image("badge.k", (img: image_t) => {
-								zui_image(img);
-								zui_end_element();
-							});
+							let img: image_t = data_get_image("badge.k");
+							zui_image(img);
+							zui_end_element();
 
 							zui_text_area(zui_handle("uimenu_14", { text: msg }), Align.Left, false);
 

+ 1 - 2
base/Sources/UIMenubar.ts

@@ -136,8 +136,7 @@ class UIMenubar {
 							scale_pos: mesh.scalePos,
 							scale_tex: mesh.scaleTex
 						};
-						let md: mesh_data_t;
-						mesh_data_create(raw, (_md: mesh_data_t) => { md = _md; });
+						let md: mesh_data_t = mesh_data_create(raw);
 						let dotPlane: mesh_object_t = scene_get_child(".Plane").ext;
 						UIMenubar._plane = mesh_object_create(md, dotPlane.materials);
 						array_remove(scene_meshes, UIMenubar._plane);

+ 5 - 6
base/Sources/UtilMesh.ts

@@ -76,12 +76,11 @@ class UtilMesh {
 		if (va3 != null) raw.vertex_arrays.push({ values: va3, attrib: "col", data: "short4norm", padding: 1 });
 
 		UtilMesh.removeMergedMesh();
-		mesh_data_create(raw, (md: mesh_data_t) => {
-			Context.raw.mergedObject = mesh_object_create(md, Context.raw.paintObject.materials);
-			Context.raw.mergedObject.base.name = Context.raw.paintObject.base.name + "_merged";
-			Context.raw.mergedObject.force_context = "paint";
-			object_set_parent(Context.raw.mergedObject.base, Context.mainObject().base);
-		});
+		let md: mesh_data_t = mesh_data_create(raw);
+		Context.raw.mergedObject = mesh_object_create(md, Context.raw.paintObject.materials);
+		Context.raw.mergedObject.base.name = Context.raw.paintObject.base.name + "_merged";
+		Context.raw.mergedObject.force_context = "paint";
+		object_set_parent(Context.raw.mergedObject.base, Context.mainObject().base);
 
 		///if (krom_direct3d12 || krom_vulkan || krom_metal)
 		RenderPathRaytrace.ready = false;

+ 21 - 23
base/Sources/UtilParticle.ts

@@ -53,31 +53,29 @@ class UtilParticle {
 			}
 		}
 
-		data_get_material("Scene", "MaterialParticle", (md: material_data_t) => {
-			Context.raw.particleMaterial = md;
-
-			for (let obj of _scene_raw.objects) {
-				if (obj.name == ".Sphere") {
-					let particle: obj_t = JSON.parse(JSON.stringify(obj));
-					particle.name = ".Particle";
-					particle.is_particle = true;
-					particle.material_refs = ["MaterialParticle"];
-					_scene_raw.objects.push(particle);
-					for (let i = 0; i < 16; ++i) particle.transform.values[i] *= 0.01;
-					break;
-				}
+		let md: material_data_t = data_get_material("Scene", "MaterialParticle");
+		Context.raw.particleMaterial = md;
+
+		for (let obj of _scene_raw.objects) {
+			if (obj.name == ".Sphere") {
+				let particle: obj_t = JSON.parse(JSON.stringify(obj));
+				particle.name = ".Particle";
+				particle.is_particle = true;
+				particle.material_refs = ["MaterialParticle"];
+				_scene_raw.objects.push(particle);
+				for (let i = 0; i < 16; ++i) particle.transform.values[i] *= 0.01;
+				break;
 			}
+		}
 
-			scene_spawn_object(".Sphere", null, (o: object_t) => {
-				let mo: mesh_object_t = o.ext;
-				mo.base.name = ".ParticleEmitter";
-				mo.base.raw = JSON.parse(JSON.stringify(mo.base.raw));
-				mo.base.raw.particle_refs = particle_refs;
-				///if arm_particles
-				mesh_object_setup_particle_system(mo, "Scene", particle_refs[0]);
-				///end
-			});
-		});
+		let o: object_t = scene_spawn_object(".Sphere");
+		let mo: mesh_object_t = o.ext;
+		mo.base.name = ".ParticleEmitter";
+		mo.base.raw = JSON.parse(JSON.stringify(mo.base.raw));
+		mo.base.raw.particle_refs = particle_refs;
+		///if arm_particles
+		mesh_object_setup_particle_system(mo, "Scene", particle_refs[0]);
+		///end
 	}
 
 	///if arm_physics

+ 15 - 16
base/Sources/main.ts

@@ -41,22 +41,21 @@ function main_start() {
 		if (Config.raw.layout == null) Base.initLayout();
 		krom_set_app_name(manifest_title);
 		app_init(function() {
-			scene_set_active("Scene", function(o: object_t) {
-				UniformsExt.init();
-				RenderPathBase.init();
-
-				if (Context.raw.renderMode == RenderMode.RenderForward) {
-					RenderPathDeferred.init(); // Allocate gbuffer
-					RenderPathForward.init();
-					render_path_commands = RenderPathForward.commands;
-				}
-				else {
-					RenderPathDeferred.init();
-					render_path_commands = RenderPathDeferred.commands;
-				}
-
-				new Base();
-			});
+			let o: object_t = scene_set_active("Scene");
+			UniformsExt.init();
+			RenderPathBase.init();
+
+			if (Context.raw.renderMode == RenderMode.RenderForward) {
+				RenderPathDeferred.init(); // Allocate gbuffer
+				RenderPathForward.init();
+				render_path_commands = RenderPathForward.commands;
+			}
+			else {
+				RenderPathDeferred.init();
+				render_path_commands = RenderPathDeferred.commands;
+			}
+
+			new Base();
 		});
 	});
 }