forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
73
leenkx/Sources/iron/data/Armature.hx
Normal file
73
leenkx/Sources/iron/data/Armature.hx
Normal file
@ -0,0 +1,73 @@
|
||||
package iron.data;
|
||||
|
||||
#if lnx_skin
|
||||
|
||||
import iron.data.SceneFormat;
|
||||
import iron.math.Mat4;
|
||||
|
||||
class Armature {
|
||||
public var uid: Int;
|
||||
public var name: String;
|
||||
public var actions: Array<TAction> = [];
|
||||
var matsReady = false;
|
||||
|
||||
public function new(uid: Int, name: String, actions: Array<TSceneFormat>) {
|
||||
this.uid = uid;
|
||||
this.name = name;
|
||||
|
||||
for (a in actions) {
|
||||
for (o in a.objects) setParents(o);
|
||||
var bones: Array<TObj> = [];
|
||||
traverseBones(a.objects, function(object: TObj) { bones.push(object); });
|
||||
this.actions.push({ name: a.name, bones: bones, mats: null });
|
||||
}
|
||||
}
|
||||
|
||||
public function initMats() {
|
||||
if (matsReady) return;
|
||||
matsReady = true;
|
||||
|
||||
for (a in actions) {
|
||||
if (a.mats != null) continue;
|
||||
a.mats = [];
|
||||
for (b in a.bones) {
|
||||
a.mats.push(Mat4.fromFloat32Array(b.transform.values));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAction(name: String): TAction {
|
||||
for (a in actions) if (a.name == name) return a;
|
||||
return null;
|
||||
}
|
||||
|
||||
static function setParents(object: TObj) {
|
||||
if (object.children == null) return;
|
||||
for (o in object.children) {
|
||||
o.parent = object;
|
||||
setParents(o);
|
||||
}
|
||||
}
|
||||
|
||||
static function traverseBones(objects: Array<TObj>, callback: TObj->Void) {
|
||||
for (i in 0...objects.length) {
|
||||
traverseBonesStep(objects[i], callback);
|
||||
}
|
||||
}
|
||||
|
||||
static function traverseBonesStep(object: TObj, callback: TObj->Void) {
|
||||
if (object.type == "bone_object") callback(object);
|
||||
if (object.children == null) return;
|
||||
for (i in 0...object.children.length) {
|
||||
traverseBonesStep(object.children[i], callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef TAction = {
|
||||
var name: String;
|
||||
var bones: Array<TObj>;
|
||||
var mats: Array<Mat4>;
|
||||
}
|
||||
|
||||
#end
|
26
leenkx/Sources/iron/data/CameraData.hx
Normal file
26
leenkx/Sources/iron/data/CameraData.hx
Normal file
@ -0,0 +1,26 @@
|
||||
package iron.data;
|
||||
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
class CameraData {
|
||||
|
||||
public var name: String;
|
||||
public var raw: TCameraData;
|
||||
|
||||
public function new(raw: TCameraData, done: CameraData->Void) {
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
done(this);
|
||||
}
|
||||
|
||||
public static function parse(name: String, id: String, done: CameraData->Void) {
|
||||
Data.getSceneRaw(name, function(format: TSceneFormat) {
|
||||
var raw: TCameraData = Data.getCameraRawByName(format.camera_datas, id);
|
||||
if (raw == null) {
|
||||
trace('Camera data "$id" not found!');
|
||||
done(null);
|
||||
}
|
||||
new CameraData(raw, done);
|
||||
});
|
||||
}
|
||||
}
|
131
leenkx/Sources/iron/data/ConstData.hx
Normal file
131
leenkx/Sources/iron/data/ConstData.hx
Normal file
File diff suppressed because one or more lines are too long
624
leenkx/Sources/iron/data/Data.hx
Normal file
624
leenkx/Sources/iron/data/Data.hx
Normal file
@ -0,0 +1,624 @@
|
||||
package iron.data;
|
||||
|
||||
import haxe.Json;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.system.LnxPack;
|
||||
import iron.system.Lz4;
|
||||
using StringTools;
|
||||
|
||||
// Global data list and asynchronous data loading
|
||||
class Data {
|
||||
|
||||
public static var cachedSceneRaws: Map<String, TSceneFormat> = new Map();
|
||||
public static var cachedMeshes: Map<String, MeshData> = new Map();
|
||||
public static var cachedLights: Map<String, LightData> = new Map();
|
||||
public static var cachedCameras: Map<String, CameraData> = new Map();
|
||||
public static var cachedMaterials: Map<String, MaterialData> = new Map();
|
||||
public static var cachedParticles: Map<String, ParticleData> = new Map();
|
||||
public static var cachedWorlds: Map<String, WorldData> = new Map();
|
||||
public static var cachedShaders: Map<String, ShaderData> = new Map();
|
||||
#if rp_probes
|
||||
public static var cachedProbes: Map<String, ProbeData> = new Map();
|
||||
#end
|
||||
|
||||
public static var cachedBlobs: Map<String, kha.Blob> = new Map();
|
||||
public static var cachedImages: Map<String, kha.Image> = new Map();
|
||||
#if lnx_audio
|
||||
public static var cachedSounds: Map<String, kha.Sound> = new Map();
|
||||
#end
|
||||
public static var cachedVideos: Map<String, kha.Video> = new Map();
|
||||
public static var cachedFonts: Map<String, kha.Font> = new Map();
|
||||
|
||||
public static var assetsLoaded = 0;
|
||||
static var loadingMeshes: Map<String, Array<MeshData->Void>> = new Map();
|
||||
static var loadingLights: Map<String, Array<LightData->Void>> = new Map();
|
||||
static var loadingCameras: Map<String, Array<CameraData->Void>> = new Map();
|
||||
static var loadingMaterials: Map<String, Array<MaterialData->Void>> = new Map();
|
||||
static var loadingParticles: Map<String, Array<ParticleData->Void>> = new Map();
|
||||
static var loadingWorlds: Map<String, Array<WorldData->Void>> = new Map();
|
||||
static var loadingShaders: Map<String, Array<ShaderData->Void>> = new Map();
|
||||
static var loadingSceneRaws: Map<String, Array<TSceneFormat->Void>> = new Map();
|
||||
#if rp_probes
|
||||
static var loadingProbes: Map<String, Array<ProbeData->Void>> = new Map();
|
||||
#end
|
||||
static var loadingBlobs: Map<String, Array<kha.Blob->Void>> = new Map();
|
||||
static var loadingImages: Map<String, Array<kha.Image->Void>> = new Map();
|
||||
#if lnx_audio
|
||||
static var loadingSounds: Map<String, Array<kha.Sound->Void>> = new Map();
|
||||
#end
|
||||
static var loadingVideos: Map<String, Array<kha.Video->Void>> = new Map();
|
||||
static var loadingFonts: Map<String, Array<kha.Font->Void>> = new Map();
|
||||
|
||||
#if krom_windows
|
||||
public static inline var sep = "\\";
|
||||
#else
|
||||
public static inline var sep = "/";
|
||||
#end
|
||||
|
||||
#if lnx_data_dir
|
||||
#if krom_android
|
||||
public static var dataPath = "data" + sep;
|
||||
#else
|
||||
public static var dataPath = "." + sep + "data" + sep;
|
||||
#end
|
||||
#else
|
||||
public static var dataPath = "";
|
||||
#end
|
||||
|
||||
public function new() {}
|
||||
|
||||
public static function deleteAll() {
|
||||
for (c in cachedMeshes) c.delete();
|
||||
cachedMeshes = new Map();
|
||||
for (c in cachedShaders) c.delete();
|
||||
cachedShaders = new Map();
|
||||
cachedSceneRaws = new Map();
|
||||
cachedLights = new Map();
|
||||
cachedCameras = new Map();
|
||||
cachedMaterials = new Map();
|
||||
cachedParticles = new Map();
|
||||
cachedWorlds = new Map();
|
||||
if (RenderPath.active != null) RenderPath.active.unload();
|
||||
|
||||
for (c in cachedBlobs) c.unload();
|
||||
cachedBlobs = new Map();
|
||||
for (c in cachedImages) c.unload();
|
||||
cachedImages = new Map();
|
||||
#if lnx_audio
|
||||
for (c in cachedSounds) c.unload();
|
||||
cachedSounds = new Map();
|
||||
#end
|
||||
for (c in cachedVideos) c.unload();
|
||||
cachedVideos = new Map();
|
||||
for (c in cachedFonts) c.unload();
|
||||
cachedFonts = new Map();
|
||||
}
|
||||
|
||||
public static function getMesh(file: String, name: String, done: MeshData->Void) {
|
||||
var handle = file + name;
|
||||
var cached = cachedMeshes.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingMeshes.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingMeshes.set(handle, [done]);
|
||||
|
||||
MeshData.parse(file, name, function(b: MeshData) {
|
||||
cachedMeshes.set(handle, b);
|
||||
b.handle = handle;
|
||||
for (f in loadingMeshes.get(handle)) f(b);
|
||||
loadingMeshes.remove(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteMesh(handle: String) {
|
||||
// Remove cached mesh
|
||||
var mesh = cachedMeshes.get(handle);
|
||||
if (mesh == null) return;
|
||||
mesh.delete();
|
||||
cachedMeshes.remove(handle);
|
||||
}
|
||||
|
||||
public static function getLight(file: String, name: String, done: LightData->Void) {
|
||||
var handle = file + name;
|
||||
var cached = cachedLights.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingLights.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingLights.set(handle, [done]);
|
||||
|
||||
LightData.parse(file, name, function(b: LightData) {
|
||||
cachedLights.set(handle, b);
|
||||
for (f in loadingLights.get(handle)) f(b);
|
||||
loadingLights.remove(handle);
|
||||
});
|
||||
}
|
||||
|
||||
#if rp_probes
|
||||
public static function getProbe(file: String, name: String, done: ProbeData->Void) {
|
||||
var handle = file + name;
|
||||
var cached = cachedProbes.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingProbes.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingProbes.set(handle, [done]);
|
||||
|
||||
ProbeData.parse(file, name, function(b: ProbeData) {
|
||||
cachedProbes.set(handle, b);
|
||||
for (f in loadingProbes.get(handle)) f(b);
|
||||
loadingProbes.remove(handle);
|
||||
});
|
||||
}
|
||||
#end
|
||||
|
||||
public static function getCamera(file: String, name: String, done: CameraData->Void) {
|
||||
var handle = file + name;
|
||||
var cached = cachedCameras.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingCameras.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingCameras.set(handle, [done]);
|
||||
|
||||
CameraData.parse(file, name, function(b: CameraData) {
|
||||
cachedCameras.set(handle, b);
|
||||
for (f in loadingCameras.get(handle)) f(b);
|
||||
loadingCameras.remove(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public static function getMaterial(file: String, name: String, done: MaterialData->Void) {
|
||||
var handle = file + name;
|
||||
var cached = cachedMaterials.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingMaterials.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingMaterials.set(handle, [done]);
|
||||
|
||||
MaterialData.parse(file, name, function(b: MaterialData) {
|
||||
cachedMaterials.set(handle, b);
|
||||
for (f in loadingMaterials.get(handle)) f(b);
|
||||
loadingMaterials.remove(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public static function getParticle(file: String, name: String, done: ParticleData->Void) {
|
||||
var handle = file + name;
|
||||
var cached = cachedParticles.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingParticles.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingParticles.set(handle, [done]);
|
||||
|
||||
ParticleData.parse(file, name, function(b: ParticleData) {
|
||||
cachedParticles.set(handle, b);
|
||||
for (f in loadingParticles.get(handle)) f(b);
|
||||
loadingParticles.remove(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public static function getWorld(file: String, name: String, done: WorldData->Void) {
|
||||
if (name == null) { // No world defined in scene
|
||||
done(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var handle = file + name;
|
||||
var cached = cachedWorlds.get(handle);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingWorlds.get(handle);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingWorlds.set(handle, [done]);
|
||||
|
||||
WorldData.parse(file, name, function(b: WorldData) {
|
||||
cachedWorlds.set(handle, b);
|
||||
for (f in loadingWorlds.get(handle)) f(b);
|
||||
loadingWorlds.remove(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public static function getShader(file: String, name: String, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
|
||||
// Only one context override per shader data for now
|
||||
var cacheName = name;
|
||||
if (overrideContext != null) cacheName += "2";
|
||||
var cached = cachedShaders.get(cacheName); // Shader must have unique name
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingShaders.get(cacheName);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingShaders.set(cacheName, [done]);
|
||||
|
||||
ShaderData.parse(file, name, function(b: ShaderData) {
|
||||
cachedShaders.set(cacheName, b);
|
||||
for (f in loadingShaders.get(cacheName)) f(b);
|
||||
loadingShaders.remove(cacheName);
|
||||
}, overrideContext);
|
||||
}
|
||||
|
||||
public static function getSceneRaw(file: String, done: TSceneFormat->Void) {
|
||||
var cached = cachedSceneRaws.get(file);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingSceneRaws.get(file);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingSceneRaws.set(file, [done]);
|
||||
|
||||
// If no extension specified, set to .arm
|
||||
var compressed = file.endsWith(".lz4");
|
||||
var isJson = file.endsWith(".json");
|
||||
var ext = (compressed || isJson || file.endsWith(".lnx")) ? "" : ".lnx";
|
||||
|
||||
getBlob(file + ext, function(b: kha.Blob) {
|
||||
var parsed: TSceneFormat = null;
|
||||
|
||||
#if lnx_compress
|
||||
if (compressed) {
|
||||
var bytes = b.toBytes();
|
||||
|
||||
// First 8 bytes contain data size for decoding
|
||||
var packedSize = haxe.Int64.toInt(bytes.getInt64(0));
|
||||
|
||||
parsed = LnxPack.decode(Lz4.decode(bytes.sub(8, bytes.length - 8), packedSize));
|
||||
}
|
||||
|
||||
else #end if (isJson) {
|
||||
var s = b.toString();
|
||||
parsed = s.charAt(0) == "{" ? Json.parse(s) : LnxPack.decode(b.toBytes());
|
||||
}
|
||||
else {
|
||||
parsed = LnxPack.decode(b.toBytes());
|
||||
}
|
||||
|
||||
returnSceneRaw(file, parsed);
|
||||
});
|
||||
}
|
||||
|
||||
static function returnSceneRaw(file: String, parsed: TSceneFormat) {
|
||||
cachedSceneRaws.set(file, parsed);
|
||||
for (f in loadingSceneRaws.get(file)) f(parsed);
|
||||
loadingSceneRaws.remove(file);
|
||||
}
|
||||
|
||||
public static function getMeshRawByName(datas: Array<TMeshData>, name: String): TMeshData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getLightRawByName(datas: Array<TLightData>, name: String): TLightData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
#if rp_probes
|
||||
public static function getProbeRawByName(datas: Array<TProbeData>, name: String): TProbeData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
public static function getCameraRawByName(datas: Array<TCameraData>, name: String): TCameraData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getMaterialRawByName(datas: Array<TMaterialData>, name: String): TMaterialData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getParticleRawByName(datas: Array<TParticleData>, name: String): TParticleData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getWorldRawByName(datas: Array<TWorldData>, name: String): TWorldData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getShaderRawByName(datas: Array<TShaderData>, name: String): TShaderData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
#if lnx_audio
|
||||
public static function getSpeakerRawByName(datas: Array<TSpeakerData>, name: String): TSpeakerData {
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
// Raw assets
|
||||
public static function getBlob(file: String, done: kha.Blob->Void) {
|
||||
var cached = cachedBlobs.get(file); // Is already cached
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingBlobs.get(file); // Is already being loaded
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingBlobs.set(file, [done]); // Start loading
|
||||
|
||||
kha.Assets.loadBlobFromPath(resolvePath(file), function(b: kha.Blob) {
|
||||
cachedBlobs.set(file, b);
|
||||
for (f in loadingBlobs.get(file)) f(b);
|
||||
loadingBlobs.remove(file);
|
||||
assetsLoaded++;
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteBlob(handle: String) {
|
||||
var blob = cachedBlobs.get(handle);
|
||||
if (blob == null) return;
|
||||
blob.unload();
|
||||
cachedBlobs.remove(handle);
|
||||
}
|
||||
|
||||
public static function getImage(file: String, done: kha.Image->Void, readable = false, format = "RGBA32") {
|
||||
#if (cpp || hl || lnx_use_k_images)
|
||||
if (!file.endsWith(".k")) {
|
||||
file = file.substring(0, file.length - 4) + ".k";
|
||||
}
|
||||
#end
|
||||
|
||||
var cached = cachedImages.get(file);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingImages.get(file);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingImages.set(file, [done]);
|
||||
|
||||
#if lnx_image_embed
|
||||
var imageBlob = cachedBlobs.get(file);
|
||||
if (imageBlob != null) {
|
||||
kha.Image.fromEncodedBytes(imageBlob.bytes, ".k", function(b: kha.Image) {
|
||||
cachedImages.set(file, b);
|
||||
for (f in loadingImages.get(file)) f(b);
|
||||
loadingImages.remove(file);
|
||||
assetsLoaded++;
|
||||
}, null, readable);
|
||||
return;
|
||||
}
|
||||
#end
|
||||
|
||||
kha.Assets.loadImageFromPath(resolvePath(file), readable, function(b: kha.Image) {
|
||||
cachedImages.set(file, b);
|
||||
for (f in loadingImages.get(file)) f(b);
|
||||
loadingImages.remove(file);
|
||||
assetsLoaded++;
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteImage(handle: String) {
|
||||
var image = cachedImages.get(handle);
|
||||
if (image == null) return;
|
||||
image.unload();
|
||||
cachedImages.remove(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
Load sound file from disk into ram.
|
||||
@param file A String matching the file name of the sound file on disk.
|
||||
@param done Completion handler function to do something after the sound is loaded.
|
||||
*/
|
||||
#if lnx_audio
|
||||
public static function getSound(file: String, done: kha.Sound->Void) {
|
||||
#if lnx_soundcompress
|
||||
if (file.endsWith(".wav")) file = file.substring(0, file.length - 4) + ".ogg";
|
||||
#end
|
||||
|
||||
var cached = cachedSounds.get(file);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingSounds.get(file);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingSounds.set(file, [done]);
|
||||
|
||||
kha.Assets.loadSoundFromPath(resolvePath(file), function(b: kha.Sound) {
|
||||
#if lnx_soundcompress
|
||||
b.uncompress(function () {
|
||||
#end
|
||||
cachedSounds.set(file, b);
|
||||
for (f in loadingSounds.get(file)) f(b);
|
||||
loadingSounds.remove(file);
|
||||
assetsLoaded++;
|
||||
#if lnx_soundcompress
|
||||
});
|
||||
#end
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteSound(handle: String) {
|
||||
var sound = cachedSounds.get(handle);
|
||||
if (sound == null) return;
|
||||
sound.unload();
|
||||
cachedSounds.remove(handle);
|
||||
}
|
||||
#end // lnx_audio
|
||||
|
||||
public static function getVideo(file: String, done: kha.Video->Void) {
|
||||
#if (cpp || hl)
|
||||
file = file.substring(0, file.length - 4) + ".avi";
|
||||
#elseif krom
|
||||
file = file.substring(0, file.length - 4) + ".webm";
|
||||
#end
|
||||
var cached = cachedVideos.get(file);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingVideos.get(file);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingVideos.set(file, [done]);
|
||||
|
||||
kha.Assets.loadVideoFromPath(resolvePath(file), function(b: kha.Video) {
|
||||
cachedVideos.set(file, b);
|
||||
for (f in loadingVideos.get(file)) f(b);
|
||||
loadingVideos.remove(file);
|
||||
assetsLoaded++;
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteVideo(handle: String) {
|
||||
var video = cachedVideos.get(handle);
|
||||
if (video == null) return;
|
||||
video.unload();
|
||||
cachedVideos.remove(handle);
|
||||
}
|
||||
|
||||
public static function getFont(file: String, done: kha.Font->Void) {
|
||||
var cached = cachedFonts.get(file);
|
||||
if (cached != null) {
|
||||
done(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
var loading = loadingFonts.get(file);
|
||||
if (loading != null) {
|
||||
loading.push(done);
|
||||
return;
|
||||
}
|
||||
|
||||
loadingFonts.set(file, [done]);
|
||||
|
||||
kha.Assets.loadFontFromPath(resolvePath(file), function(b: kha.Font) {
|
||||
cachedFonts.set(file, b);
|
||||
for (f in loadingFonts.get(file)) f(b);
|
||||
loadingFonts.remove(file);
|
||||
assetsLoaded++;
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteFont(handle: String) {
|
||||
var font = cachedFonts.get(handle);
|
||||
if (font == null) return;
|
||||
font.unload();
|
||||
cachedFonts.remove(handle);
|
||||
}
|
||||
|
||||
public static function isAbsolute(file: String): Bool {
|
||||
return file.charAt(0) == "/" || file.charAt(1) == ":" || file.charAt(4) == ":" || (file.charAt(0) == "\\" && file.charAt(1) == "\\");
|
||||
}
|
||||
|
||||
static inline function isUp(file: String): Bool {
|
||||
return file.charAt(0) == "." && file.charAt(1) == ".";
|
||||
}
|
||||
|
||||
/**
|
||||
Extract filename from path.
|
||||
*/
|
||||
static inline function baseName(path: String): String {
|
||||
var slash = path.lastIndexOf(sep);
|
||||
return slash >= 0 ? path.substr(slash + 1) : path;
|
||||
}
|
||||
|
||||
static inline function resolvePath(file: String): String {
|
||||
if (isAbsolute(file) || isUp(file)) return file;
|
||||
#if lnx_data_dir
|
||||
return dataPath + file;
|
||||
#else
|
||||
return baseName(file);
|
||||
#end
|
||||
}
|
||||
}
|
390
leenkx/Sources/iron/data/Geometry.hx
Normal file
390
leenkx/Sources/iron/data/Geometry.hx
Normal file
@ -0,0 +1,390 @@
|
||||
package iron.data;
|
||||
|
||||
import haxe.ds.Vector;
|
||||
import kha.graphics4.VertexBuffer;
|
||||
import kha.graphics4.IndexBuffer;
|
||||
import kha.graphics4.Usage;
|
||||
import kha.graphics4.VertexStructure;
|
||||
import kha.graphics4.VertexData;
|
||||
import kha.arrays.ByteArray;
|
||||
import kha.arrays.Float32Array;
|
||||
import kha.arrays.Uint32Array;
|
||||
import kha.arrays.Int16Array;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Mat4;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.data.MeshData;
|
||||
|
||||
class Geometry {
|
||||
#if lnx_deinterleaved
|
||||
public var vertexBuffers: Vector<InterleavedVertexBuffer>;
|
||||
#else
|
||||
public var vertexBuffer: VertexBuffer;
|
||||
public var vertexBufferMap: Map<String, VertexBuffer> = new Map();
|
||||
#end
|
||||
public var indexBuffers: Array<IndexBuffer>;
|
||||
public var start = 0; // For drawIndexedVertices
|
||||
public var count = -1;
|
||||
public var name = "";
|
||||
|
||||
public var ready = false;
|
||||
public var vertices: ByteArray;
|
||||
public var indices: Array<Uint32Array>;
|
||||
public var numTris = 0;
|
||||
public var materialIndices: Array<Int>;
|
||||
public var struct: VertexStructure;
|
||||
public var structLength: Int;
|
||||
public var structStr: String;
|
||||
public var usage: Usage;
|
||||
|
||||
public var instancedVB: VertexBuffer = null;
|
||||
public var instanced = false;
|
||||
public var instanceCount = 0;
|
||||
|
||||
public var positions: TVertexArray;
|
||||
public var normals: TVertexArray;
|
||||
public var uvs: TVertexArray;
|
||||
public var cols: TVertexArray;
|
||||
public var vertexArrays: Array<TVertexArray>;
|
||||
var data: MeshData;
|
||||
|
||||
public var aabb: Vec4 = null;
|
||||
public var aabbMin: Vec4 = null;
|
||||
public var aabbMax: Vec4 = null;
|
||||
|
||||
// Skinned
|
||||
#if lnx_skin
|
||||
public var skeletonTransformsI: Array<Mat4> = null;
|
||||
#end
|
||||
|
||||
public function new(data: MeshData, indices: Array<Uint32Array>, materialIndices: Array<Int>, usage: Usage = null) {
|
||||
if (usage == null) usage = Usage.StaticUsage;
|
||||
|
||||
this.indices = indices;
|
||||
this.materialIndices = materialIndices;
|
||||
this.usage = usage;
|
||||
|
||||
this.vertexArrays = data.raw.vertex_arrays;
|
||||
this.positions = getVArray('pos');
|
||||
this.normals = getVArray('nor');
|
||||
this.uvs = getVArray('tex');
|
||||
this.cols = getVArray('col');
|
||||
this.data = data;
|
||||
|
||||
struct = getVertexStructure(vertexArrays);
|
||||
structLength = Std.int(struct.byteSize() / 2);
|
||||
structStr = "";
|
||||
for (e in struct.elements) structStr += e.name;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
#if lnx_deinterleaved
|
||||
for (buf in vertexBuffers) if (buf.buffer != null) buf.buffer.delete();
|
||||
#else
|
||||
for (buf in vertexBufferMap) if (buf != null) buf.delete();
|
||||
#end
|
||||
for (buf in indexBuffers) buf.delete();
|
||||
}
|
||||
|
||||
static function getVertexStructure(vertexArrays: Array<TVertexArray>): VertexStructure {
|
||||
var structure = new VertexStructure();
|
||||
for (i in 0...vertexArrays.length) {
|
||||
structure.add(vertexArrays[i].attrib, getVertexData(vertexArrays[i].data));
|
||||
}
|
||||
return structure;
|
||||
}
|
||||
|
||||
static function getVertexData(data: String): VertexData {
|
||||
switch (data) {
|
||||
case "short4norm": return VertexData.Short4Norm;
|
||||
case "short2norm": return VertexData.Short2Norm;
|
||||
default: return VertexData.Short4Norm;
|
||||
}
|
||||
}
|
||||
|
||||
public function applyScale(sx: Float, sy: Float, sz: Float) {
|
||||
data.scalePos *= sx;
|
||||
}
|
||||
|
||||
public function getVArray(name: String): TVertexArray {
|
||||
for (i in 0...vertexArrays.length) {
|
||||
if (vertexArrays[i].attrib == name) {
|
||||
return vertexArrays[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setupInstanced(data: Float32Array, instancedType: Int, usage: Usage) {
|
||||
var structure = new VertexStructure();
|
||||
structure.instanced = true;
|
||||
instanced = true;
|
||||
// pos, pos+rot, pos+scale, pos+rot+scale
|
||||
structure.add("ipos", kha.graphics4.VertexData.Float3);
|
||||
if (instancedType == 2 || instancedType == 4) {
|
||||
structure.add("irot", kha.graphics4.VertexData.Float3);
|
||||
}
|
||||
if (instancedType == 3 || instancedType == 4) {
|
||||
structure.add("iscl", kha.graphics4.VertexData.Float3);
|
||||
}
|
||||
|
||||
instanceCount = Std.int(data.length / Std.int(structure.byteSize() / 4));
|
||||
instancedVB = new VertexBuffer(instanceCount, structure, usage, 1);
|
||||
var vertices = instancedVB.lock();
|
||||
for (i in 0...Std.int(vertices.byteLength / 4)) vertices.setFloat32(i * 4, data[i]);
|
||||
instancedVB.unlock();
|
||||
}
|
||||
|
||||
public function copyVertices(vertices: ByteArray, offset = 0, fakeUVs = false) {
|
||||
buildVertices(vertices, vertexArrays, offset, fakeUVs);
|
||||
}
|
||||
|
||||
static function buildVertices(vertices: ByteArray, vertexArrays: Array<TVertexArray>, offset = 0, fakeUVs = false, uvsIndex = -1) {
|
||||
var numVertices = verticesCount(vertexArrays[0]);
|
||||
var di = -1 + offset;
|
||||
for (i in 0...numVertices) {
|
||||
for (va in 0...vertexArrays.length) {
|
||||
var l = vertexArrays[va].size;
|
||||
if (fakeUVs && va == uvsIndex) { // Add fake uvs if uvs where "asked" for but not found
|
||||
for (j in 0...l) vertices.setInt16(++di * 2, 0);
|
||||
continue;
|
||||
}
|
||||
for (o in 0...l) {
|
||||
vertices.setInt16(++di * 2, vertexArrays[va].values[i * l + o]);
|
||||
}
|
||||
if (vertexArrays[va].padding != null) {
|
||||
if (vertexArrays[va].padding == 1) {
|
||||
vertices.setInt16(++di * 2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getVerticesLength(): Int {
|
||||
var res = 0;
|
||||
for (i in 0...vertexArrays.length) {
|
||||
res += vertexArrays[i].values.length;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#if lnx_deinterleaved
|
||||
public function get(vs: Array<TVertexElement>): Array<VertexBuffer> {
|
||||
var vbs = [];
|
||||
for (e in vs) {
|
||||
for (v in 0...vertexBuffers.length)
|
||||
if (vertexBuffers[v].name == e.name) {
|
||||
vbs.push(vertexBuffers[v].buffer);
|
||||
continue;
|
||||
}
|
||||
if (e.name == "ipos" && instancedVB != null) {
|
||||
vbs.push(instancedVB);
|
||||
}
|
||||
}
|
||||
return vbs;
|
||||
}
|
||||
#else
|
||||
|
||||
public function get(vs: Array<TVertexElement>): VertexBuffer {
|
||||
var key = "";
|
||||
for (e in vs) key += e.name;
|
||||
var vb = vertexBufferMap.get(key);
|
||||
if (vb == null) {
|
||||
var nVertexArrays = [];
|
||||
var atex = false;
|
||||
var texOffset = -1;
|
||||
var acol = false;
|
||||
for (e in 0...vs.length) {
|
||||
if (vs[e].name == "tex") {
|
||||
atex = true;
|
||||
texOffset = e;
|
||||
}
|
||||
if (vs[e].name == "col") {
|
||||
acol = true;
|
||||
}
|
||||
for (va in 0...vertexArrays.length) {
|
||||
if (vs[e].name == vertexArrays[va].attrib) {
|
||||
nVertexArrays.push(vertexArrays[va]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Multi-mat mesh with different vertex structures
|
||||
var struct = getVertexStructure(nVertexArrays);
|
||||
vb = new VertexBuffer(Std.int(positions.values.length / positions.size), struct, usage);
|
||||
vertices = vb.lock();
|
||||
buildVertices(vertices, nVertexArrays, 0, atex && uvs == null, texOffset);
|
||||
vb.unlock();
|
||||
vertexBufferMap.set(key, vb);
|
||||
if (atex && uvs == null) trace("Leenkx Warning: Geometry " + name + " is missing UV map");
|
||||
if (acol && cols == null) trace("Leenkx Warning: Geometry " + name + " is missing vertex colors");
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
#end
|
||||
|
||||
public function build() {
|
||||
if (ready) return;
|
||||
|
||||
#if lnx_deinterleaved
|
||||
var vaLength = vertexArrays.length;
|
||||
vertexBuffers = new Vector(vaLength);
|
||||
for (i in 0...vaLength)
|
||||
vertexBuffers[i] = {
|
||||
name: vertexArrays[i].attrib,
|
||||
buffer: makeDeinterleavedVB(vertexArrays[i].values, vertexArrays[i].attrib, vertexArrays[i].size)
|
||||
};
|
||||
#else
|
||||
|
||||
vertexBuffer = new VertexBuffer(Std.int(positions.values.length / positions.size), struct, usage);
|
||||
vertices = vertexBuffer.lock();
|
||||
buildVertices(vertices, vertexArrays);
|
||||
vertexBuffer.unlock();
|
||||
vertexBufferMap.set(structStr, vertexBuffer);
|
||||
#end
|
||||
|
||||
indexBuffers = [];
|
||||
for (id in indices) {
|
||||
if (id.length == 0) continue;
|
||||
var indexBuffer = new IndexBuffer(id.length, usage);
|
||||
numTris += Std.int(id.length / 3);
|
||||
|
||||
var indicesA = indexBuffer.lock();
|
||||
for (i in 0...indicesA.length) indicesA[i] = id[i];
|
||||
|
||||
indexBuffer.unlock();
|
||||
indexBuffers.push(indexBuffer);
|
||||
}
|
||||
|
||||
// Instanced
|
||||
if (data.raw.instanced_data != null) setupInstanced(data.raw.instanced_data, data.raw.instanced_type, usage);
|
||||
|
||||
ready = true;
|
||||
}
|
||||
|
||||
#if lnx_deinterleaved
|
||||
function makeDeinterleavedVB(data: ByteArray, name: String, structLength: Int): VertexBuffer {
|
||||
var struct = new VertexStructure();
|
||||
struct.add(name, structLength == 2 ? VertexData.Short2Norm : VertexData.Short4Norm);
|
||||
|
||||
var vertexBuffer = new VertexBuffer(Std.int(data.byteLength / 2 / structLength), struct, usage);
|
||||
|
||||
var vertices = vertexBuffer.lock();
|
||||
for (i in 0...Std.int(vertices.byteLength / 2)) vertices.setInt16(i * 2, data.getInt16(i * 2));
|
||||
vertexBuffer.unlock();
|
||||
|
||||
return vertexBuffer;
|
||||
}
|
||||
#end
|
||||
|
||||
public function getVerticesCount(): Int {
|
||||
return Std.int(positions.values.length / positions.size);
|
||||
}
|
||||
|
||||
inline static function verticesCount(arr: TVertexArray): Int {
|
||||
return Std.int(arr.values.length / arr.size);
|
||||
}
|
||||
|
||||
// Skinned
|
||||
#if lnx_skin
|
||||
public function initSkeletonTransforms(transformsI: Array<Float32Array>) {
|
||||
skeletonTransformsI = [];
|
||||
for (t in transformsI) {
|
||||
var mi = Mat4.fromFloat32Array(t);
|
||||
skeletonTransformsI.push(mi);
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
public function calculateAABB() {
|
||||
aabbMin = new Vec4(-0.01, -0.01, -0.01);
|
||||
aabbMax = new Vec4(0.01, 0.01, 0.01);
|
||||
aabb = new Vec4();
|
||||
var i = 0;
|
||||
while (i < positions.values.length) {
|
||||
if (positions.values[i ] > aabbMax.x) aabbMax.x = positions.values[i];
|
||||
if (positions.values[i + 1] > aabbMax.y) aabbMax.y = positions.values[i + 1];
|
||||
if (positions.values[i + 2] > aabbMax.z) aabbMax.z = positions.values[i + 2];
|
||||
if (positions.values[i ] < aabbMin.x) aabbMin.x = positions.values[i];
|
||||
if (positions.values[i + 1] < aabbMin.y) aabbMin.y = positions.values[i + 1];
|
||||
if (positions.values[i + 2] < aabbMin.z) aabbMin.z = positions.values[i + 2];
|
||||
i += 4;
|
||||
}
|
||||
aabb.x = (Math.abs(aabbMin.x) + Math.abs(aabbMax.x)) / 32767 * data.scalePos;
|
||||
aabb.y = (Math.abs(aabbMin.y) + Math.abs(aabbMax.y)) / 32767 * data.scalePos;
|
||||
aabb.z = (Math.abs(aabbMin.z) + Math.abs(aabbMax.z)) / 32767 * data.scalePos;
|
||||
}
|
||||
|
||||
public function calculateTangents() {
|
||||
// var num_verts = Std.int(positions.length / 4);
|
||||
// var tangents = new Float32Array(num_verts * 3);
|
||||
// var bitangents = new Float32Array(num_verts * 3);
|
||||
// for (ia in indices) {
|
||||
// var num_tris = Std.int(ia.length / 3);
|
||||
// for (i in 0...num_tris) {
|
||||
// var i0 = ia[i * 3 ];
|
||||
// var i1 = ia[i * 3 + 1];
|
||||
// var i2 = ia[i * 3 + 2];
|
||||
// var v0 = Vector((positions[i0 * 4], positions[i0 * 4 + 1], positions[i0 * 4 + 2]));
|
||||
// var v1 = Vector((positions[i1 * 4], positions[i1 * 4 + 1], positions[i1 * 4 + 2]));
|
||||
// var v2 = Vector((positions[i2 * 4], positions[i2 * 4 + 1], positions[i2 * 4 + 2]));
|
||||
// var uv0 = Vector((uvs[i0 * 2], uvs[i0 * 2 + 1]));
|
||||
// var uv1 = Vector((uvs[i1 * 2], uvs[i1 * 2 + 1]));
|
||||
// var uv2 = Vector((uvs[i2 * 2], uvs[i2 * 2 + 1]));
|
||||
|
||||
// var deltaPos1 = v1 - v0;
|
||||
// var deltaPos2 = v2 - v0;
|
||||
// var deltaUV1 = uv1 - uv0;
|
||||
// var deltaUV2 = uv2 - uv0;
|
||||
// var d = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
|
||||
// var r = d != 0 ? 1.0 / d : 1.0;
|
||||
// var tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r;
|
||||
// var bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r;
|
||||
|
||||
// var tangents[i0 * 3 ] += tangent.x;
|
||||
// var tangents[i0 * 3 + 1] += tangent.y;
|
||||
// var tangents[i0 * 3 + 2] += tangent.z;
|
||||
// var tangents[i1 * 3 ] += tangent.x;
|
||||
// var tangents[i1 * 3 + 1] += tangent.y;
|
||||
// var tangents[i1 * 3 + 2] += tangent.z;
|
||||
// var tangents[i2 * 3 ] += tangent.x;
|
||||
// var tangents[i2 * 3 + 1] += tangent.y;
|
||||
// var tangents[i2 * 3 + 2] += tangent.z;
|
||||
// var bitangents[i0 * 3 ] += bitangent.x;
|
||||
// var bitangents[i0 * 3 + 1] += bitangent.y;
|
||||
// var bitangents[i0 * 3 + 2] += bitangent.z;
|
||||
// var bitangents[i1 * 3 ] += bitangent.x;
|
||||
// var bitangents[i1 * 3 + 1] += bitangent.y;
|
||||
// var bitangents[i1 * 3 + 2] += bitangent.z;
|
||||
// var bitangents[i2 * 3 ] += bitangent.x;
|
||||
// var bitangents[i2 * 3 + 1] += bitangent.y;
|
||||
// var bitangents[i2 * 3 + 2] += bitangent.z;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Orthogonalize
|
||||
// for (i in 0...num_verts) {
|
||||
// var t = Vector((tangents[i * 3], tangents[i * 3 + 1], tangents[i * 3 + 2]));
|
||||
// var b = Vector((bitangents[i * 3], bitangents[i * 3 + 1], bitangents[i * 3 + 2]));
|
||||
// var n = Vector((normals[i * 2], normals[i * 2 + 1], positions[i * 4 + 3] / scale_pos));
|
||||
// var v = t - n * n.dot(t);
|
||||
// v.normalize();
|
||||
// // Calculate handedness
|
||||
// var cnv = n.cross(v);
|
||||
// if (cnv.dot(b) < 0.0)
|
||||
// v = v * -1.0;
|
||||
// tangents[i * 3 ] = v.x;
|
||||
// tangents[i * 3 + 1] = v.y;
|
||||
// tangents[i * 3 + 2] = v.z;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef InterleavedVertexBuffer = {
|
||||
#else
|
||||
@:structInit class InterleavedVertexBuffer {
|
||||
#end
|
||||
public var name: String;
|
||||
public var buffer: VertexBuffer;
|
||||
}
|
36
leenkx/Sources/iron/data/LightData.hx
Normal file
36
leenkx/Sources/iron/data/LightData.hx
Normal file
@ -0,0 +1,36 @@
|
||||
package iron.data;
|
||||
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
class LightData {
|
||||
|
||||
public var name: String;
|
||||
public var raw: TLightData;
|
||||
|
||||
public function new(raw: TLightData, done: LightData->Void) {
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
done(this);
|
||||
}
|
||||
|
||||
public static inline function typeToInt(s: String): Int {
|
||||
switch (s) {
|
||||
case "sun": return 0;
|
||||
case "point": return 1;
|
||||
case "spot": return 2;
|
||||
case "area": return 3;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function parse(name: String, id: String, done: LightData->Void) {
|
||||
Data.getSceneRaw(name, function(format: TSceneFormat) {
|
||||
var raw: TLightData = Data.getLightRawByName(format.light_datas, id);
|
||||
if (raw == null) {
|
||||
trace('Light data "$id" not found!');
|
||||
done(null);
|
||||
}
|
||||
new LightData(raw, done);
|
||||
});
|
||||
}
|
||||
}
|
144
leenkx/Sources/iron/data/MaterialData.hx
Normal file
144
leenkx/Sources/iron/data/MaterialData.hx
Normal file
@ -0,0 +1,144 @@
|
||||
package iron.data;
|
||||
|
||||
import haxe.ds.Vector;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.data.ShaderData;
|
||||
import iron.object.MeshObject;
|
||||
|
||||
class MaterialData {
|
||||
|
||||
static var uidCounter = 0;
|
||||
public var uid: Float;
|
||||
public var name: String;
|
||||
public var raw: TMaterialData;
|
||||
public var shader: ShaderData;
|
||||
|
||||
public var contexts: Array<MaterialContext> = null;
|
||||
|
||||
public function new(raw: TMaterialData, done: MaterialData->Void, file = "") {
|
||||
uid = ++uidCounter; // Start from 1
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
|
||||
var ref = raw.shader.split("/");
|
||||
var object_file = "";
|
||||
var data_ref = "";
|
||||
if (ref.length == 2) { // File reference
|
||||
object_file = ref[0];
|
||||
data_ref = ref[1];
|
||||
}
|
||||
else { // Local data
|
||||
object_file = file;
|
||||
data_ref = raw.shader;
|
||||
}
|
||||
|
||||
Data.getShader(object_file, data_ref, function(b: ShaderData) {
|
||||
shader = b;
|
||||
|
||||
// Contexts have to be in the same order as in raw data for now
|
||||
contexts = [];
|
||||
while (contexts.length < raw.contexts.length) contexts.push(null);
|
||||
var contextsLoaded = 0;
|
||||
|
||||
for (i in 0...raw.contexts.length) {
|
||||
var c = raw.contexts[i];
|
||||
new MaterialContext(c, function(self: MaterialContext) {
|
||||
contexts[i] = self;
|
||||
contextsLoaded++;
|
||||
if (contextsLoaded == raw.contexts.length) done(this);
|
||||
});
|
||||
}
|
||||
}, raw.override_context);
|
||||
}
|
||||
|
||||
public static function parse(file: String, name: String, done: MaterialData->Void) {
|
||||
Data.getSceneRaw(file, function(format: TSceneFormat) {
|
||||
var raw: TMaterialData = Data.getMaterialRawByName(format.material_datas, name);
|
||||
if (raw == null) {
|
||||
trace('Material data "$name" not found!');
|
||||
done(null);
|
||||
}
|
||||
new MaterialData(raw, done, file);
|
||||
});
|
||||
}
|
||||
|
||||
public function getContext(name: String): MaterialContext {
|
||||
for (c in contexts) {
|
||||
// 'mesh' will fetch both 'mesh' and 'meshheight' contexts
|
||||
if (c.raw.name.substr(0, name.length) == name) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class MaterialContext {
|
||||
public var raw: TMaterialContext;
|
||||
public var textures: Vector<kha.Image> = null;
|
||||
public var id = 0;
|
||||
static var num = 0;
|
||||
|
||||
public function new(raw: TMaterialContext, done: MaterialContext->Void) {
|
||||
this.raw = raw;
|
||||
id = num++;
|
||||
|
||||
if (raw.bind_textures != null && raw.bind_textures.length > 0) {
|
||||
|
||||
textures = new Vector(raw.bind_textures.length);
|
||||
var texturesLoaded = 0;
|
||||
|
||||
for (i in 0...raw.bind_textures.length) {
|
||||
var tex = raw.bind_textures[i];
|
||||
|
||||
if (tex.file == "" || tex.source == "movie") { // Empty texture
|
||||
texturesLoaded++;
|
||||
if (texturesLoaded == raw.bind_textures.length) done(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
Data.getImage(tex.file, function(image: kha.Image) {
|
||||
textures[i] = image;
|
||||
texturesLoaded++;
|
||||
|
||||
// Set mipmaps
|
||||
if (tex.mipmaps != null) {
|
||||
var mipmaps: Array<kha.Image> = [];
|
||||
while (mipmaps.length < tex.mipmaps.length) mipmaps.push(null);
|
||||
var mipmapsLoaded = 0;
|
||||
|
||||
for (j in 0...tex.mipmaps.length) {
|
||||
var name = tex.mipmaps[j];
|
||||
|
||||
Data.getImage(name, function(mipimg: kha.Image) {
|
||||
mipmaps[j] = mipimg;
|
||||
mipmapsLoaded++;
|
||||
|
||||
if (mipmapsLoaded == tex.mipmaps.length) {
|
||||
image.setMipmaps(mipmaps);
|
||||
tex.mipmaps = null;
|
||||
tex.generate_mipmaps = false;
|
||||
|
||||
if (texturesLoaded == raw.bind_textures.length) done(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (tex.generate_mipmaps == true && image != null) {
|
||||
image.generateMipmaps(1000);
|
||||
tex.mipmaps = null;
|
||||
tex.generate_mipmaps = false;
|
||||
|
||||
if (texturesLoaded == raw.bind_textures.length) done(this);
|
||||
}
|
||||
else if (texturesLoaded == raw.bind_textures.length) done(this);
|
||||
|
||||
}, false, tex.format != null ? tex.format : "RGBA32");
|
||||
}
|
||||
}
|
||||
else done(this);
|
||||
}
|
||||
|
||||
public function setTextureParameters(g: kha.graphics4.Graphics, textureIndex: Int, context: ShaderContext, unitIndex: Int) {
|
||||
// This function is called by MeshObject for samplers set using material context
|
||||
context.setTextureParameters(g, unitIndex, raw.bind_textures[textureIndex]);
|
||||
}
|
||||
}
|
274
leenkx/Sources/iron/data/MeshBatch.hx
Normal file
274
leenkx/Sources/iron/data/MeshBatch.hx
Normal file
@ -0,0 +1,274 @@
|
||||
package iron.data;
|
||||
|
||||
#if lnx_batch
|
||||
|
||||
import kha.arrays.ByteArray;
|
||||
import kha.graphics4.VertexBuffer;
|
||||
import kha.graphics4.IndexBuffer;
|
||||
import kha.graphics4.Usage;
|
||||
import kha.graphics4.VertexStructure;
|
||||
import kha.graphics4.Graphics;
|
||||
import iron.object.MeshObject;
|
||||
import iron.object.Uniforms;
|
||||
import iron.data.Geometry;
|
||||
import iron.data.MaterialData;
|
||||
import iron.data.ShaderData;
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
@:access(iron.object.MeshObject)
|
||||
class MeshBatch {
|
||||
|
||||
var buckets: Map<ShaderData, Bucket> = new Map();
|
||||
public var nonBatched: Array<MeshObject> = [];
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function remove() {
|
||||
for (b in buckets) b.remove();
|
||||
}
|
||||
|
||||
public static function isBatchable(m: MeshObject): Bool {
|
||||
m.computeDepthRead();
|
||||
var batch =
|
||||
m.materials != null &&
|
||||
m.materials.length == 1 &&
|
||||
!m.data.geom.instanced &&
|
||||
!m.data.isSkinned &&
|
||||
m.data.raw.morph_target == null &&
|
||||
!m.depthRead;
|
||||
return batch;
|
||||
}
|
||||
|
||||
public function addMesh(m: MeshObject, isLod: Bool): Bool {
|
||||
// No instancing, multimat, skinning, morph targets or lod batching
|
||||
if (!isBatchable(m) || isLod) {
|
||||
nonBatched.push(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
var shader = m.materials[0].shader;
|
||||
var b = buckets.get(shader);
|
||||
if (b == null) {
|
||||
b = new Bucket(shader);
|
||||
buckets.set(shader, b);
|
||||
}
|
||||
b.addMesh(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function removeMesh(m: MeshObject) {
|
||||
var shader = m.materials[0].shader;
|
||||
var b = buckets.get(shader);
|
||||
if (b != null) b.removeMesh(m);
|
||||
else nonBatched.remove(m);
|
||||
}
|
||||
|
||||
@:access(iron.RenderPath)
|
||||
public function render(g: Graphics, context: String, bindParams: Array<String>) {
|
||||
|
||||
for (b in buckets) {
|
||||
if (!b.batched) b.batch();
|
||||
if (b.meshes.length > 0 && b.meshes[0].cullMaterial(context)) continue;
|
||||
|
||||
var scontext = b.shader.getContext(context);
|
||||
g.setPipeline(scontext.pipeState);
|
||||
// #if lnx_deinterleaved // TODO
|
||||
// g.setVertexBuffers(b.vertexBuffers);
|
||||
// #else
|
||||
g.setVertexBuffer(b.getVertexBuffer(scontext.raw.vertex_elements));
|
||||
// #end
|
||||
g.setIndexBuffer(b.indexBuffer);
|
||||
|
||||
Uniforms.setContextConstants(g, scontext, bindParams);
|
||||
|
||||
RenderPath.sortMeshesDistance(b.meshes);
|
||||
|
||||
for (m in b.meshes) {
|
||||
if (!m.visible) continue; // Skip render if object is hidden
|
||||
if (m.cullMesh(context, Scene.active.camera, RenderPath.active.light)) continue;
|
||||
|
||||
// Get context
|
||||
var materialContexts: Array<MaterialContext> = [];
|
||||
var shaderContexts: Array<ShaderContext> = [];
|
||||
m.getContexts(context, m.materials, materialContexts, shaderContexts);
|
||||
|
||||
Uniforms.posUnpack = m.data.scalePos;
|
||||
Uniforms.texUnpack = m.data.scaleTex;
|
||||
m.transform.update();
|
||||
|
||||
// Render mesh
|
||||
Uniforms.setObjectConstants(g, scontext, m);
|
||||
Uniforms.setMaterialConstants(g, scontext, materialContexts[0]);
|
||||
|
||||
g.drawIndexedVertices(m.data.start, m.data.count);
|
||||
|
||||
#if lnx_veloc
|
||||
m.prevMatrix.setFrom(m.transform.worldUnpack);
|
||||
#end
|
||||
|
||||
#if lnx_debug
|
||||
RenderPath.drawCalls++;
|
||||
RenderPath.batchCalls++;
|
||||
#end
|
||||
}
|
||||
|
||||
#if lnx_debug
|
||||
RenderPath.batchBuckets++;
|
||||
#end
|
||||
}
|
||||
|
||||
// Render non-batched meshes
|
||||
inline RenderPath.meshRenderLoop(g, context, bindParams, nonBatched);
|
||||
}
|
||||
}
|
||||
|
||||
class Bucket {
|
||||
|
||||
public var batched = false;
|
||||
public var shader: ShaderData;
|
||||
var vertexBuffer: VertexBuffer;
|
||||
var vertexBufferMap: Map<String, VertexBuffer> = new Map();
|
||||
public var indexBuffer: IndexBuffer;
|
||||
public var meshes: Array<MeshObject> = [];
|
||||
|
||||
public function new(shader: ShaderData) {
|
||||
this.shader = shader;
|
||||
}
|
||||
|
||||
public function remove() {
|
||||
indexBuffer.delete();
|
||||
// this.vertexBuffer is in the map, so it's also deleted here
|
||||
for (buf in vertexBufferMap) buf.delete();
|
||||
meshes = [];
|
||||
}
|
||||
|
||||
public function addMesh(m: MeshObject) {
|
||||
meshes.push(m);
|
||||
}
|
||||
|
||||
public function removeMesh(m: MeshObject) {
|
||||
meshes.remove(m);
|
||||
}
|
||||
|
||||
function copyAttribute(attribSize: Int, count: Int, to: ByteArray, toStride: Int, toOffset: Int, from: ByteArray, fromStride: Int, fromOffset: Int) {
|
||||
for (i in 0...count) {
|
||||
for (j in 0...attribSize) {
|
||||
to.setInt16((i * toStride + toOffset + j) * 2, from.getInt16((i * fromStride + fromOffset + j) * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractVertexBuffer(elems: Array<TVertexElement>): VertexBuffer {
|
||||
// Build vertex buffer for specific context
|
||||
var vs = new VertexStructure();
|
||||
for (e in elems) vs.add(e.name, ShaderContext.parseData(e.data));
|
||||
|
||||
var vb = new VertexBuffer(vertexBuffer.count(), vs, Usage.StaticUsage);
|
||||
var to = vb.lock();
|
||||
var from = vertexBuffer.lock();
|
||||
|
||||
var toOffset = 0;
|
||||
var toStride = Std.int(vb.stride() / 2);
|
||||
var fromOffset = 0;
|
||||
var fromStride = Std.int(vertexBuffer.stride() / 2);
|
||||
|
||||
for (e in elems) {
|
||||
var size = 0;
|
||||
if (e.name == "pos") { size = 4; fromOffset = 0; }
|
||||
else if (e.name == "nor") { size = 2; fromOffset = 4; }
|
||||
else if (e.name == "tex") { size = 2; fromOffset = 6; }
|
||||
copyAttribute(size, vertexBuffer.count(), to, toStride, toOffset, from, fromStride, fromOffset);
|
||||
toOffset += size;
|
||||
}
|
||||
|
||||
vb.unlock();
|
||||
return vb;
|
||||
}
|
||||
|
||||
public function getVertexBuffer(elems: Array<TVertexElement>): VertexBuffer {
|
||||
var s = "";
|
||||
for (e in elems) s += e.name;
|
||||
var vb = vertexBufferMap.get(s);
|
||||
if (vb == null) {
|
||||
vb = extractVertexBuffer(elems);
|
||||
vertexBufferMap.set(s, vb);
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
||||
function vertexCount(g: Geometry, hasUVs: Bool): Int {
|
||||
var vcount = g.getVerticesLength();
|
||||
if (hasUVs && g.uvs == null) {
|
||||
vcount += Std.int(g.positions.values.length / 4) * 2;
|
||||
}
|
||||
return vcount;
|
||||
}
|
||||
|
||||
public function batch() {
|
||||
batched = true;
|
||||
|
||||
// Ensure same vertex structure for batched meshes
|
||||
var hasUVs = false;
|
||||
for (m in meshes) {
|
||||
if (m.data.geom.uvs != null) {
|
||||
hasUVs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unique mesh datas
|
||||
var vcount = 0;
|
||||
var icount = 0;
|
||||
var mdatas: Array<MeshData> = [];
|
||||
for (m in meshes) {
|
||||
var mdFound = false;
|
||||
for (md in mdatas) {
|
||||
if (m.data == md) {
|
||||
mdFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mdFound) {
|
||||
mdatas.push(m.data);
|
||||
m.data.start = icount;
|
||||
m.data.count = m.data.geom.indices[0].length;
|
||||
icount += m.data.count;
|
||||
vcount += vertexCount(m.data.geom, hasUVs);
|
||||
}
|
||||
}
|
||||
|
||||
if (mdatas.length == 0) return;
|
||||
|
||||
// Pick UVs if present
|
||||
var vs = mdatas[0].geom.struct;
|
||||
for (md in mdatas) if (md.geom.struct.size() > vs.size()) vs = md.geom.struct;
|
||||
|
||||
// Build shared buffers
|
||||
vertexBuffer = new VertexBuffer(vcount, vs, Usage.StaticUsage);
|
||||
var vertices = vertexBuffer.lock();
|
||||
var offset = 0;
|
||||
for (md in mdatas) {
|
||||
md.geom.copyVertices(vertices, offset, hasUVs);
|
||||
offset += vertexCount(md.geom, hasUVs);
|
||||
}
|
||||
vertexBuffer.unlock();
|
||||
|
||||
var s = "";
|
||||
for (e in vs.elements) s += e.name;
|
||||
vertexBufferMap.set(s, vertexBuffer);
|
||||
|
||||
indexBuffer = new IndexBuffer(icount, Usage.StaticUsage);
|
||||
var indices = indexBuffer.lock();
|
||||
var di = -1;
|
||||
var offset = 0;
|
||||
for (md in mdatas) {
|
||||
for (i in 0...md.geom.indices[0].length) {
|
||||
indices[++di] = md.geom.indices[0][i] + offset;
|
||||
}
|
||||
offset += Std.int(md.geom.getVerticesLength() / md.geom.structLength);
|
||||
}
|
||||
indexBuffer.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
129
leenkx/Sources/iron/data/MeshData.hx
Normal file
129
leenkx/Sources/iron/data/MeshData.hx
Normal file
@ -0,0 +1,129 @@
|
||||
package iron.data;
|
||||
|
||||
import kha.graphics4.Usage;
|
||||
import kha.graphics4.VertexData;
|
||||
import kha.arrays.Int16Array;
|
||||
import kha.arrays.Uint32Array;
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
class MeshData {
|
||||
|
||||
public var name: String;
|
||||
public var raw: TMeshData;
|
||||
public var format: TSceneFormat;
|
||||
public var geom: Geometry;
|
||||
public var start = 0; // Batched
|
||||
public var count = -1;
|
||||
public var refcount = 0; // Number of users
|
||||
public var handle: String; // Handle used to retrieve this object in Data
|
||||
public var scalePos: kha.FastFloat = 1.0;
|
||||
public var scaleTex: kha.FastFloat = 1.0;
|
||||
public var isSkinned: Bool;
|
||||
|
||||
public function new(raw: TMeshData, done: MeshData->Void) {
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
|
||||
if (raw.scale_pos != null) scalePos = raw.scale_pos;
|
||||
if (raw.scale_tex != null) scaleTex = raw.scale_tex;
|
||||
|
||||
// Mesh data
|
||||
var indices: Array<Uint32Array> = [];
|
||||
var materialIndices: Array<Int> = [];
|
||||
for (ind in raw.index_arrays) {
|
||||
indices.push(ind.values);
|
||||
materialIndices.push(ind.material);
|
||||
}
|
||||
|
||||
// Skinning
|
||||
isSkinned = raw.skin != null;
|
||||
|
||||
// Prepare vertex array for skinning and fill size data
|
||||
var vertexArrays = raw.vertex_arrays;
|
||||
if (isSkinned) {
|
||||
vertexArrays.push({ attrib: "bone", values: null, data: "short4norm" });
|
||||
vertexArrays.push({ attrib: "weight", values: null, data: "short4norm" });
|
||||
}
|
||||
for (i in 0...vertexArrays.length) {
|
||||
vertexArrays[i].size = getVertexSize(vertexArrays[i].data, getPadding(vertexArrays[i].padding));
|
||||
}
|
||||
|
||||
// Usage, also used for instanced data
|
||||
var parsedUsage = Usage.StaticUsage;
|
||||
if (raw.dynamic_usage != null && raw.dynamic_usage == true) parsedUsage = Usage.DynamicUsage;
|
||||
var usage = parsedUsage;
|
||||
|
||||
if (isSkinned) {
|
||||
var bonea = null;
|
||||
var weighta = null;
|
||||
var vertex_length = Std.int(vertexArrays[0].values.length / vertexArrays[0].size);
|
||||
var l = vertex_length * 4;
|
||||
bonea = new Int16Array(l);
|
||||
weighta = new Int16Array(l);
|
||||
|
||||
var index = 0;
|
||||
var ai = 0;
|
||||
for (i in 0...vertex_length) {
|
||||
var boneCount = raw.skin.bone_count_array[i];
|
||||
for (j in index...(index + boneCount)) {
|
||||
bonea[ai] = raw.skin.bone_index_array[j];
|
||||
weighta[ai] = raw.skin.bone_weight_array[j];
|
||||
ai++;
|
||||
}
|
||||
// Fill unused weights
|
||||
for (j in boneCount...4) {
|
||||
bonea[ai] = 0;
|
||||
weighta[ai] = 0;
|
||||
ai++;
|
||||
}
|
||||
index += boneCount;
|
||||
}
|
||||
vertexArrays[vertexArrays.length - 2].values = bonea;
|
||||
vertexArrays[vertexArrays.length - 1].values = weighta;
|
||||
}
|
||||
|
||||
// Make vertex buffers
|
||||
geom = new Geometry(this, indices, materialIndices, usage);
|
||||
geom.name = name;
|
||||
|
||||
done(this);
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
geom.delete();
|
||||
}
|
||||
|
||||
public static function parse(name: String, id: String, done: MeshData->Void) {
|
||||
Data.getSceneRaw(name, function(format: TSceneFormat) {
|
||||
var raw: TMeshData = Data.getMeshRawByName(format.mesh_datas, id);
|
||||
if (raw == null) {
|
||||
trace('Mesh data "$id" not found!');
|
||||
done(null);
|
||||
}
|
||||
|
||||
new MeshData(raw, function(dat: MeshData) {
|
||||
dat.format = format;
|
||||
// Skinned
|
||||
#if lnx_skin
|
||||
if (raw.skin != null) {
|
||||
dat.geom.initSkeletonTransforms(raw.skin.transformsI);
|
||||
}
|
||||
#end
|
||||
done(dat);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getVertexSize(vertex_data: String, padding: Int = 0): Int {
|
||||
switch (vertex_data) {
|
||||
case "short4norm": return 4 - padding;
|
||||
case "short2norm": return 2 - padding;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline function getPadding(padding: Null<Int>): Int {
|
||||
return padding != null ? padding : 0;
|
||||
}
|
||||
}
|
||||
|
27
leenkx/Sources/iron/data/ParticleData.hx
Normal file
27
leenkx/Sources/iron/data/ParticleData.hx
Normal file
@ -0,0 +1,27 @@
|
||||
package iron.data;
|
||||
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
class ParticleData {
|
||||
|
||||
public var name: String;
|
||||
public var raw: TParticleData;
|
||||
|
||||
public function new(raw: TParticleData, done: ParticleData->Void) {
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
|
||||
done(this);
|
||||
}
|
||||
|
||||
public static function parse(name: String, id: String, done: ParticleData->Void) {
|
||||
Data.getSceneRaw(name, function(format: TSceneFormat) {
|
||||
var raw: TParticleData = Data.getParticleRawByName(format.particle_datas, id);
|
||||
if (raw == null) {
|
||||
trace('Particle data "$id" not found!');
|
||||
done(null);
|
||||
}
|
||||
new ParticleData(raw, done);
|
||||
});
|
||||
}
|
||||
}
|
27
leenkx/Sources/iron/data/ProbeData.hx
Normal file
27
leenkx/Sources/iron/data/ProbeData.hx
Normal file
@ -0,0 +1,27 @@
|
||||
package iron.data;
|
||||
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
class ProbeData {
|
||||
|
||||
#if rp_probes
|
||||
|
||||
public var raw: TProbeData;
|
||||
|
||||
public function new(raw: TProbeData, done: ProbeData->Void) {
|
||||
this.raw = raw;
|
||||
done(this);
|
||||
}
|
||||
|
||||
public static function parse(name: String, id: String, done: ProbeData->Void) {
|
||||
Data.getSceneRaw(name, function(format: TSceneFormat) {
|
||||
var raw: TProbeData = Data.getProbeRawByName(format.probe_datas, id);
|
||||
if (raw == null) {
|
||||
trace('Probe data "$id" not found!');
|
||||
done(null);
|
||||
}
|
||||
new ProbeData(raw, done);
|
||||
});
|
||||
}
|
||||
#end
|
||||
}
|
578
leenkx/Sources/iron/data/SceneFormat.hx
Normal file
578
leenkx/Sources/iron/data/SceneFormat.hx
Normal file
@ -0,0 +1,578 @@
|
||||
package iron.data;
|
||||
|
||||
#if !macro
|
||||
import kha.FastFloat;
|
||||
import kha.arrays.Float32Array;
|
||||
import kha.arrays.Uint32Array;
|
||||
import kha.arrays.Int16Array;
|
||||
#end
|
||||
|
||||
#if macro
|
||||
typedef Float32Array = haxe.io.Float32Array;
|
||||
typedef Uint32Array = haxe.io.UInt32Array;
|
||||
typedef Int16Array = haxe.io.UInt16Array;
|
||||
typedef FastFloat = Float;
|
||||
#end
|
||||
|
||||
#if js
|
||||
typedef TSceneFormat = {
|
||||
#else
|
||||
@:structInit class TSceneFormat {
|
||||
#end
|
||||
@:optional public var name: String;
|
||||
@:optional public var mesh_datas: Array<TMeshData>;
|
||||
@:optional public var light_datas: Array<TLightData>;
|
||||
@:optional public var probe_datas: Array<TProbeData>;
|
||||
@:optional public var camera_datas: Array<TCameraData>;
|
||||
@:optional public var camera_ref: String; // Active camera
|
||||
@:optional public var material_datas: Array<TMaterialData>;
|
||||
@:optional public var particle_datas: Array<TParticleData>;
|
||||
@:optional public var shader_datas: Array<TShaderData>;
|
||||
@:optional public var speaker_datas: Array<TSpeakerData>;
|
||||
@:optional public var world_datas: Array<TWorldData>;
|
||||
@:optional public var world_ref: String;
|
||||
@:optional public var tilesheet_datas: Array<TTilesheetData>;
|
||||
@:optional public var objects: Array<TObj>;
|
||||
@:optional public var groups: Array<TGroup>;
|
||||
@:optional public var gravity: Float32Array;
|
||||
@:optional public var traits: Array<TTrait>; // Scene root traits
|
||||
@:optional public var embedded_datas: Array<String>; // Preload for this scene, images only for now
|
||||
@:optional public var frame_time: Null<FastFloat>;
|
||||
@:optional public var irradiance: Float32Array; // Blob with spherical harmonics, bands 0,1,2
|
||||
@:optional public var terrain_datas: Array<TTerrainData>;
|
||||
@:optional public var terrain_ref: String;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TMeshData = {
|
||||
#else
|
||||
@:structInit class TMeshData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var vertex_arrays: Array<TVertexArray>;
|
||||
public var index_arrays: Array<TIndexArray>;
|
||||
@:optional public var dynamic_usage: Null<Bool>;
|
||||
@:optional public var skin: TSkin;
|
||||
@:optional public var instanced_data: Float32Array;
|
||||
@:optional public var instanced_type: Null<Int>; // off, loc, loc+rot, loc+scale, loc+rot+scale
|
||||
@:optional public var scale_pos: Null<FastFloat>; // Unpack pos from (-1,1) coords
|
||||
@:optional public var scale_tex: Null<FastFloat>; // Unpack tex from (-1,1) coords
|
||||
@:optional public var morph_target: TMorphTarget;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TMorphTarget = {
|
||||
#else
|
||||
@:structInit class TMorphTarget {
|
||||
#end
|
||||
public var morph_target_data_file: String;
|
||||
public var morph_scale: FastFloat;
|
||||
public var morph_offset: FastFloat;
|
||||
public var num_morph_targets: Int;
|
||||
public var morph_img_size: Int;
|
||||
public var morph_block_size: Int;
|
||||
public var morph_target_ref: Array<String>;
|
||||
public var morph_target_defaults: Float32Array;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TSkin = {
|
||||
#else
|
||||
@:structInit class TSkin {
|
||||
#end
|
||||
public var transform: TTransform;
|
||||
public var bone_ref_array: Array<String>;
|
||||
public var bone_len_array: Float32Array;
|
||||
public var transformsI: Array<Float32Array>; // per-bone, size = 16, with skin.transform, pre-inverted
|
||||
public var bone_count_array: Int16Array;
|
||||
public var bone_index_array: Int16Array;
|
||||
public var bone_weight_array: Int16Array;
|
||||
public var constraints: Array<TConstraint>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TVertexArray = {
|
||||
#else
|
||||
@:structInit class TVertexArray {
|
||||
#end
|
||||
public var attrib: String;
|
||||
public var values: Int16Array;
|
||||
public var data: String; // short4norm, short2norm
|
||||
@:optional public var padding: Null<Int>;
|
||||
@:optional public var size: Null<Int>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TIndexArray = {
|
||||
#else
|
||||
@:structInit class TIndexArray {
|
||||
#end
|
||||
public var values: Uint32Array; // size = 3
|
||||
public var material: Int;
|
||||
@:optional public var vertex_map: Uint32Array; // size = 3
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TLightData = {
|
||||
#else
|
||||
@:structInit class TLightData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var type: String; // sun, point, spot
|
||||
public var color: Float32Array;
|
||||
public var strength: FastFloat;
|
||||
@:optional public var cast_shadow: Null<Bool>;
|
||||
@:optional public var near_plane: Null<FastFloat>;
|
||||
@:optional public var far_plane: Null<FastFloat>;
|
||||
@:optional public var fov: Null<FastFloat>;
|
||||
@:optional public var shadows_bias: Null<FastFloat>;
|
||||
@:optional public var shadowmap_size: Null<Int>;
|
||||
@:optional public var shadowmap_cube: Null<Bool>; // Omni shadows for point
|
||||
@:optional public var spot_size: Null<FastFloat>;
|
||||
@:optional public var spot_blend: Null<FastFloat>;
|
||||
@:optional public var light_size: Null<FastFloat>; // Shadow soft size
|
||||
@:optional public var size: Null<FastFloat>; // Area light
|
||||
@:optional public var size_y: Null<FastFloat>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TCameraData = {
|
||||
#else
|
||||
@:structInit class TCameraData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var near_plane: FastFloat;
|
||||
public var far_plane: FastFloat;
|
||||
public var fov: FastFloat;
|
||||
@:optional public var clear_color: Float32Array;
|
||||
@:optional public var aspect: Null<FastFloat>;
|
||||
@:optional public var frustum_culling: Null<Bool>;
|
||||
@:optional public var ortho: Float32Array; // Indicates ortho camera, left, right, bottom, top
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TMaterialData = {
|
||||
#else
|
||||
@:structInit class TMaterialData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var shader: String;
|
||||
public var contexts: Array<TMaterialContext>;
|
||||
@:optional public var skip_context: String;
|
||||
@:optional public var override_context: TShaderOverride;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TShaderOverride = {
|
||||
#else
|
||||
@:structInit class TShaderOverride {
|
||||
#end
|
||||
@:optional public var cull_mode: String;
|
||||
@:optional public var addressing: String;
|
||||
@:optional public var filter: String;
|
||||
@:optional public var shared_sampler: String;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TMaterialContext = {
|
||||
#else
|
||||
@:structInit class TMaterialContext {
|
||||
#end
|
||||
public var name: String;
|
||||
@:optional public var depth_read: Null<Bool>;
|
||||
@:optional public var bind_constants: Array<TBindConstant>;
|
||||
@:optional public var bind_textures: Array<TBindTexture>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TBindConstant = {
|
||||
#else
|
||||
@:structInit class TBindConstant {
|
||||
#end
|
||||
public var name: String;
|
||||
@:optional public var vec4Value: Float32Array;
|
||||
@:optional public var vec3Value: Float32Array;
|
||||
@:optional public var vec2Value: Float32Array;
|
||||
@:optional public var floatValue: Null<FastFloat>;
|
||||
@:optional public var boolValue: Null<Bool>;
|
||||
@:optional public var intValue: Null<Int>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TBindTexture = {
|
||||
#else
|
||||
@:structInit class TBindTexture {
|
||||
#end
|
||||
public var name: String;
|
||||
public var file: String;
|
||||
@:optional public var format: String; // RGBA32, RGBA64, R8
|
||||
@:optional public var generate_mipmaps: Null<Bool>;
|
||||
@:optional public var mipmaps: Array<String>; // Reference image names
|
||||
@:optional public var u_addressing: String;
|
||||
@:optional public var v_addressing: String;
|
||||
@:optional public var min_filter: String;
|
||||
@:optional public var mag_filter: String;
|
||||
@:optional public var mipmap_filter: String;
|
||||
@:optional public var source: String; // file, movie
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TShaderData = {
|
||||
#else
|
||||
@:structInit class TShaderData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var contexts: Array<TShaderContext>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TShaderContext = {
|
||||
#else
|
||||
@:structInit class TShaderContext {
|
||||
#end
|
||||
public var name: String;
|
||||
public var depth_write: Bool;
|
||||
public var compare_mode: String;
|
||||
public var cull_mode: String;
|
||||
public var vertex_elements: Array<TVertexElement>;
|
||||
public var vertex_shader: String;
|
||||
public var fragment_shader: String;
|
||||
@:optional public var geometry_shader: String;
|
||||
@:optional public var tesscontrol_shader: String;
|
||||
@:optional public var tesseval_shader: String;
|
||||
@:optional public var constants: Array<TShaderConstant>;
|
||||
@:optional public var texture_units: Array<TTextureUnit>;
|
||||
@:optional public var blend_source: String;
|
||||
@:optional public var blend_destination: String;
|
||||
@:optional public var blend_operation: String;
|
||||
@:optional public var alpha_blend_source: String;
|
||||
@:optional public var alpha_blend_destination: String;
|
||||
@:optional public var alpha_blend_operation: String;
|
||||
@:optional public var color_writes_red: Array<Bool>; // Per target masks
|
||||
@:optional public var color_writes_green: Array<Bool>;
|
||||
@:optional public var color_writes_blue: Array<Bool>;
|
||||
@:optional public var color_writes_alpha: Array<Bool>;
|
||||
@:optional public var color_attachments: Array<String>; // RGBA32, RGBA64, R8
|
||||
@:optional public var depth_attachment: String; // DEPTH32
|
||||
@:optional public var conservative_raster: Null<Bool>;
|
||||
@:optional public var shader_from_source: Null<Bool>; // Build shader at runtime using fromSource()
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TVertexElement = {
|
||||
#else
|
||||
@:structInit class TVertexElement {
|
||||
#end
|
||||
public var name: String;
|
||||
public var data: String; // "float4", "short2norm"
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TShaderConstant = {
|
||||
#else
|
||||
@:structInit class TShaderConstant {
|
||||
#end
|
||||
public var name: String;
|
||||
public var type: String;
|
||||
@:optional public var link: String;
|
||||
@:optional public var vec4Value: Float32Array;
|
||||
@:optional public var vec3Value: Float32Array;
|
||||
@:optional public var vec2Value: Float32Array;
|
||||
@:optional public var floatValue: Null<FastFloat>;
|
||||
@:optional public var boolValue: Null<Bool>;
|
||||
@:optional public var intValue: Null<Int>;
|
||||
@:optional public var is_lnx_parameter: Null<Bool>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTextureUnit = {
|
||||
#else
|
||||
@:structInit class TTextureUnit {
|
||||
#end
|
||||
public var name: String;
|
||||
@:optional public var is_image: Null<Bool>; // image2D
|
||||
@:optional public var link: String;
|
||||
@:optional public var addressing_u: String;
|
||||
@:optional public var addressing_v: String;
|
||||
@:optional public var filter_min: String;
|
||||
@:optional public var filter_mag: String;
|
||||
@:optional public var mipmap_filter: String;
|
||||
@:optional public var default_image_file: String;
|
||||
@:optional public var is_lnx_parameter: Null<Bool>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TSpeakerData = {
|
||||
#else
|
||||
@:structInit class TSpeakerData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var sound: String;
|
||||
public var muted: Bool;
|
||||
public var loop: Bool;
|
||||
public var stream: Bool;
|
||||
public var volume: FastFloat;
|
||||
public var pitch: FastFloat;
|
||||
public var volume_min: FastFloat;
|
||||
public var volume_max: FastFloat;
|
||||
public var attenuation: FastFloat;
|
||||
public var distance_max: FastFloat;
|
||||
public var distance_reference: FastFloat;
|
||||
public var play_on_start: Bool;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTerrainData = {
|
||||
#else
|
||||
@:structInit class TTerrainData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var sectors_x: Int;
|
||||
public var sectors_y: Int;
|
||||
public var sector_size: FastFloat;
|
||||
public var height_scale: FastFloat;
|
||||
public var material_ref: String;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TWorldData = {
|
||||
#else
|
||||
@:structInit class TWorldData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var background_color: Int;
|
||||
public var probe: TProbeData;
|
||||
@:optional public var sun_direction: Float32Array; // Sky data
|
||||
@:optional public var turbidity: Null<FastFloat>;
|
||||
@:optional public var ground_albedo: Null<FastFloat>;
|
||||
@:optional public var envmap: String;
|
||||
@:optional public var nishita_density: Float32Array; // Rayleigh, Mie, ozone
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TProbeData = {
|
||||
#else
|
||||
@:structInit class TProbeData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var type: String; // grid, planar, cubemap
|
||||
public var strength: FastFloat;
|
||||
@:optional public var irradiance: String; // Reference to TIrradiance blob
|
||||
@:optional public var radiance: String;
|
||||
@:optional public var radiance_mipmaps: Null<Int>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTilesheetData = {
|
||||
#else
|
||||
@:structInit class TTilesheetData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var tilesx: Int;
|
||||
public var tilesy: Int;
|
||||
public var framerate: Int;
|
||||
public var actions: Array<TTilesheetAction>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTilesheetAction = {
|
||||
#else
|
||||
@:structInit class TTilesheetAction {
|
||||
#end
|
||||
public var name: String;
|
||||
public var start: Int;
|
||||
public var end: Int;
|
||||
public var loop: Bool;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TParticleData = {
|
||||
#else
|
||||
@:structInit class TParticleData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var type: Int; // 0 - Emitter, Hair
|
||||
public var loop: Bool;
|
||||
public var count: Int;
|
||||
public var frame_start: FastFloat;
|
||||
public var frame_end: FastFloat;
|
||||
public var lifetime: FastFloat;
|
||||
public var lifetime_random: FastFloat;
|
||||
public var emit_from: Int; // 0 - Vert, 1 - Face, 2 - Volume
|
||||
public var object_align_factor: Float32Array;
|
||||
public var factor_random: FastFloat;
|
||||
public var physics_type: Int; // 0 - No, 1 - Newton
|
||||
public var particle_size: FastFloat; // Object scale
|
||||
public var size_random: FastFloat; // Random scale
|
||||
public var mass: FastFloat;
|
||||
public var instance_object: String; // Object reference
|
||||
public var weight_gravity: FastFloat;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TParticleReference = {
|
||||
#else
|
||||
@:structInit class TParticleReference {
|
||||
#end
|
||||
public var name: String;
|
||||
public var particle: String;
|
||||
public var seed: Int;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TObj = {
|
||||
#else
|
||||
@:structInit class TObj {
|
||||
#end
|
||||
public var type: String; // object, mesh_object, light_object, camera_object, speaker_object, decal_object
|
||||
public var name: String;
|
||||
public var data_ref: String;
|
||||
public var transform: TTransform;
|
||||
@:optional public var material_refs: Array<String>;
|
||||
@:optional public var particle_refs: Array<TParticleReference>;
|
||||
@:optional public var render_emitter: Bool;
|
||||
@:optional public var is_particle: Null<Bool>; // This object is used as a particle object
|
||||
@:optional public var children: Array<TObj>;
|
||||
@:optional public var group_ref: String; // instance_type
|
||||
@:optional public var lods: Array<TLod>;
|
||||
@:optional public var lod_material: Null<Bool>;
|
||||
@:optional public var traits: Array<TTrait>;
|
||||
@:optional public var properties: Array<TProperty>;
|
||||
@:optional public var vertex_groups: Array<TVertex_groups>;
|
||||
@:optional public var constraints: Array<TConstraint>;
|
||||
@:optional public var dimensions: Float32Array; // Geometry objects
|
||||
@:optional public var object_actions: Array<String>;
|
||||
@:optional public var bone_actions: Array<String>;
|
||||
@:optional public var anim: TAnimation; // Bone/object animation
|
||||
@:optional public var parent: TObj;
|
||||
@:optional public var parent_bone: String;
|
||||
@:optional public var parent_bone_tail: Float32Array; // Translate from head to tail
|
||||
@:optional public var parent_bone_tail_pose: Float32Array;
|
||||
@:optional public var parent_bone_connected: Null<Bool>;
|
||||
@:optional public var bone_length: FastFloat;
|
||||
@:optional public var visible: Null<Bool>;
|
||||
@:optional public var visible_mesh: Null<Bool>;
|
||||
@:optional public var visible_shadow: Null<Bool>;
|
||||
@:optional public var mobile: Null<Bool>;
|
||||
@:optional public var spawn: Null<Bool>; // Auto add object when creating scene
|
||||
@:optional public var local_only: Null<Bool>; // Apply parent matrix
|
||||
@:optional public var tilesheet_ref: String;
|
||||
@:optional public var tilesheet_action_ref: String;
|
||||
@:optional public var sampled: Null<Bool>; // Object action
|
||||
@:optional public var is_ik_fk_only: Null<Bool>; // Bone IK or FK only
|
||||
@:optional public var bone_layers: Array<Bool>; // Bone Layer
|
||||
@:optional public var relative_bone_constraints: Null<Bool>; // Use parent relative bone constraints
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TProperty = {
|
||||
#else
|
||||
@:structInit class TProperty {
|
||||
#end
|
||||
public var name: String;
|
||||
public var value: Dynamic;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TVertex_groups = {
|
||||
#else
|
||||
@:structInit class TVertex_groups {
|
||||
#end
|
||||
public var name: String;
|
||||
public var value: Dynamic;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TGroup = {
|
||||
#else
|
||||
@:structInit class TGroup {
|
||||
#end
|
||||
public var name: String;
|
||||
public var instance_offset: Float32Array;
|
||||
public var object_refs: Array<String>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TLod = {
|
||||
#else
|
||||
@:structInit class TLod {
|
||||
#end
|
||||
public var object_ref: String; // Empty when limiting draw distance
|
||||
public var screen_size: FastFloat; // (0-1) size compared to lod0
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TConstraint = {
|
||||
#else
|
||||
@:structInit class TConstraint {
|
||||
#end
|
||||
public var name: String;
|
||||
public var type: String;
|
||||
@:optional public var bone: String; // Bone constraint
|
||||
@:optional public var target: String;
|
||||
@:optional public var use_x: Null<Bool>;
|
||||
@:optional public var use_y: Null<Bool>;
|
||||
@:optional public var use_z: Null<Bool>;
|
||||
@:optional public var invert_x: Null<Bool>;
|
||||
@:optional public var invert_y: Null<Bool>;
|
||||
@:optional public var invert_z: Null<Bool>;
|
||||
@:optional public var use_offset: Null<Bool>;
|
||||
@:optional public var influence: Null<FastFloat>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTrait = {
|
||||
#else
|
||||
@:structInit class TTrait {
|
||||
#end
|
||||
public var type: String;
|
||||
public var class_name: String;
|
||||
@:optional public var parameters: Array<String>; // constructor params
|
||||
@:optional public var props: Array<Dynamic>; // name - type - value list
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTransform = {
|
||||
#else
|
||||
@:structInit class TTransform {
|
||||
#end
|
||||
@:optional public var target: String;
|
||||
public var values: Float32Array;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TAnimation = {
|
||||
#else
|
||||
@:structInit class TAnimation {
|
||||
#end
|
||||
public var tracks: Array<TTrack>;
|
||||
@:optional public var begin: Null<Int>; // Frames, for non-sampled
|
||||
@:optional public var end: Null<Int>;
|
||||
@:optional public var has_delta: Null<Bool>; // Delta transform
|
||||
@:optional public var marker_frames: Uint32Array;
|
||||
@:optional public var marker_names: Array<String>;
|
||||
@:optional public var root_motion_pos: Null<Bool>;
|
||||
@:optional public var root_motion_rot: Null<Bool>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TAnimationTransform = {
|
||||
#else
|
||||
@:structInit class TAnimationTransform {
|
||||
#end
|
||||
public var type: String; // translation, translation_x, ...
|
||||
@:optional public var name: String;
|
||||
@:optional public var values: Float32Array; // translation
|
||||
@:optional public var value: Null<FastFloat>; // translation_x
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TTrack = {
|
||||
#else
|
||||
@:structInit class TTrack {
|
||||
#end
|
||||
public var target: String;
|
||||
public var frames: Uint32Array;
|
||||
public var values: Float32Array; // sampled - full matrix transforms, non-sampled - values
|
||||
@:optional public var ref_values: Array<Array<String>>; // ref values
|
||||
}
|
124
leenkx/Sources/iron/data/SceneStream.hx
Normal file
124
leenkx/Sources/iron/data/SceneStream.hx
Normal file
@ -0,0 +1,124 @@
|
||||
package iron.data;
|
||||
|
||||
#if lnx_stream
|
||||
|
||||
import haxe.ds.Vector;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.data.MaterialData;
|
||||
import iron.object.Object;
|
||||
import iron.object.MeshObject;
|
||||
import iron.object.CameraObject;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class SceneStream {
|
||||
|
||||
var checkMax = 64; // Objects checked per frame
|
||||
var checkPos = 0;
|
||||
var loadMax = 8; // Max objects loaded at once
|
||||
var loading = 0; // Objects being loaded
|
||||
var loadDistance = -1;
|
||||
var unloadDistance = -1;
|
||||
var sectors: Array<StreamSector>; // 100x100 groups
|
||||
|
||||
public function sceneTotal(): Int {
|
||||
return sectors[0].handles.length;
|
||||
}
|
||||
|
||||
public function new() {
|
||||
sectors = [new StreamSector()];
|
||||
}
|
||||
|
||||
public function remove() {}
|
||||
|
||||
public function add(object_file: String, data_ref: String, sceneName: String, armature: Armature, materials: Vector<MaterialData>, parent: Object, parentObject:TObj, obj: TObj) {
|
||||
sectors[0].handles.push({ object_file: object_file, data_ref: data_ref, sceneName: sceneName, armature: armature, materials: materials, parent: parent, parentObject: parentObject, obj: obj, object: null, loading: false });
|
||||
}
|
||||
|
||||
function setup(camera: CameraObject) {
|
||||
loadDistance = Std.int(camera.data.raw.far_plane * 1.1);
|
||||
unloadDistance = Std.int(camera.data.raw.far_plane * 1.5);
|
||||
}
|
||||
|
||||
public function update(camera: CameraObject) {
|
||||
if (loadDistance == -1) setup(camera);
|
||||
|
||||
if (loading >= loadMax) return; // Busy loading..
|
||||
|
||||
var sec = sectors[0];
|
||||
var to = Std.int(Math.min(checkMax, sec.handles.length));
|
||||
for (i in 0...to) {
|
||||
|
||||
var h = sec.handles[checkPos];
|
||||
checkPos++;
|
||||
if (checkPos >= sec.handles.length) checkPos = 0;
|
||||
|
||||
// Check radius in sector
|
||||
var camX = camera.transform.worldx();
|
||||
var camY = camera.transform.worldy();
|
||||
var camZ = camera.transform.worldz();
|
||||
var hx = h.obj.transform.values[3];
|
||||
var hy = h.obj.transform.values[7];
|
||||
var hz = h.obj.transform.values[11];
|
||||
var cameraDistance = Vec4.distancef(camX, camY, camZ, hx, hy, hz);
|
||||
var dim = h.obj.dimensions;
|
||||
if (dim != null) {
|
||||
var r = dim[0];
|
||||
if (dim[1] > r) r = dim[1];
|
||||
if (dim[2] > r) r = dim[2];
|
||||
cameraDistance -= r;
|
||||
// TODO: handle scale & rot
|
||||
}
|
||||
|
||||
if (cameraDistance < loadDistance && h.object == null && !h.loading) { // Load mesh
|
||||
// Wait for the parent object to be added to scene
|
||||
if (h.parent == null) {
|
||||
if (Scene.active.getChild(h.parentObject.name) == null) return;
|
||||
h.parent = Scene.active.getChild(h.parentObject.name);
|
||||
}
|
||||
|
||||
// Start loading
|
||||
h.loading = true;
|
||||
loading++;
|
||||
iron.Scene.active.returnMeshObject(h.object_file, h.data_ref, h.sceneName, h.armature, h.materials, h.parent, h.parentObject, h.obj, function(object: Object) {
|
||||
h.object = cast(object, MeshObject);
|
||||
h.loading = false;
|
||||
loading--;
|
||||
});
|
||||
if (loading >= loadMax) return;
|
||||
}
|
||||
else if (cameraDistance > unloadDistance && h.object != null) { // Unload mesh
|
||||
// Remove objects
|
||||
h.object.remove();
|
||||
if (h.object.data.refcount <= 0) {
|
||||
iron.data.Data.deleteMesh(h.object_file + h.data_ref);
|
||||
}
|
||||
h.object = null;
|
||||
|
||||
// Clear parents
|
||||
if (h.parent.name != Scene.active.raw.name) {
|
||||
h.parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef TMeshHandle = {
|
||||
var object_file: String;
|
||||
var data_ref: String;
|
||||
var sceneName: String;
|
||||
var armature: Armature;
|
||||
var materials: Vector<MaterialData>;
|
||||
var parent: Object;
|
||||
var parentObject: TObj;
|
||||
var obj: TObj;
|
||||
var object: MeshObject;
|
||||
var loading: Bool;
|
||||
}
|
||||
|
||||
class StreamSector {
|
||||
public function new() {}
|
||||
public var handles: Array<TMeshHandle> = []; // Mesh objects
|
||||
}
|
||||
|
||||
#end
|
401
leenkx/Sources/iron/data/ShaderData.hx
Normal file
401
leenkx/Sources/iron/data/ShaderData.hx
Normal file
@ -0,0 +1,401 @@
|
||||
package iron.data;
|
||||
|
||||
import kha.graphics4.PipelineState;
|
||||
import kha.graphics4.ConstantLocation;
|
||||
import kha.graphics4.TextureUnit;
|
||||
import kha.graphics4.VertexStructure;
|
||||
import kha.graphics4.VertexData;
|
||||
import kha.graphics4.CompareMode;
|
||||
import kha.graphics4.CullMode;
|
||||
import kha.graphics4.BlendingOperation;
|
||||
import kha.graphics4.BlendingFactor;
|
||||
import kha.graphics4.TextureAddressing;
|
||||
import kha.graphics4.TextureFilter;
|
||||
import kha.graphics4.MipMapFilter;
|
||||
import kha.graphics4.VertexShader;
|
||||
import kha.graphics4.FragmentShader;
|
||||
import kha.graphics4.TextureFormat;
|
||||
import kha.graphics4.DepthStencilFormat;
|
||||
import iron.data.SceneFormat;
|
||||
using StringTools;
|
||||
|
||||
class ShaderData {
|
||||
|
||||
public var name: String;
|
||||
public var raw: TShaderData;
|
||||
public var contexts: Array<ShaderContext> = [];
|
||||
|
||||
#if (lnx_noembed && kha_krom)
|
||||
public static var shaderPath = "../krom-resources/";
|
||||
public static inline var shaderExt = #if kha_vulkan ".spirv" #elseif (krom_android || krom_wasm) ".essl" #elseif kha_opengl ".glsl" #elseif kha_metal ".metal" #else ".d3d11" #end ;
|
||||
#end
|
||||
|
||||
public function new(raw: TShaderData, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
|
||||
for (c in raw.contexts) contexts.push(null);
|
||||
var contextsLoaded = 0;
|
||||
|
||||
for (i in 0...raw.contexts.length) {
|
||||
var c = raw.contexts[i];
|
||||
new ShaderContext(c, function(con: ShaderContext) {
|
||||
contexts[i] = con;
|
||||
contextsLoaded++;
|
||||
if (contextsLoaded == raw.contexts.length) done(this);
|
||||
}, overrideContext);
|
||||
}
|
||||
}
|
||||
|
||||
public static function parse(file: String, name: String, done: ShaderData->Void, overrideContext: TShaderOverride = null) {
|
||||
Data.getSceneRaw(file, function(format: TSceneFormat) {
|
||||
var raw: TShaderData = Data.getShaderRawByName(format.shader_datas, name);
|
||||
if (raw == null) {
|
||||
trace('Shader data "$name" not found!');
|
||||
done(null);
|
||||
}
|
||||
new ShaderData(raw, done, overrideContext);
|
||||
});
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
for (c in contexts) c.delete();
|
||||
}
|
||||
|
||||
public function getContext(name: String): ShaderContext {
|
||||
for (c in contexts) if (c.raw.name == name) return c;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class ShaderContext {
|
||||
public var raw: TShaderContext;
|
||||
public var pipeState: PipelineState;
|
||||
public var constants: Array<ConstantLocation>;
|
||||
public var textureUnits: Array<TextureUnit>;
|
||||
public var overrideContext: TShaderOverride;
|
||||
|
||||
var structure: VertexStructure;
|
||||
var instancingType = 0;
|
||||
|
||||
public function new(raw: TShaderContext, done: ShaderContext->Void, overrideContext: TShaderOverride = null) {
|
||||
this.raw = raw;
|
||||
#if (!rp_voxels)
|
||||
if (raw.name == "voxel") {
|
||||
done(this);
|
||||
return;
|
||||
}
|
||||
#end
|
||||
this.overrideContext = overrideContext;
|
||||
parseVertexStructure();
|
||||
compile(done);
|
||||
}
|
||||
|
||||
public function compile(done: ShaderContext->Void) {
|
||||
if (pipeState != null) pipeState.delete();
|
||||
pipeState = new PipelineState();
|
||||
constants = [];
|
||||
textureUnits = [];
|
||||
|
||||
if (instancingType > 0) { // Instancing
|
||||
var instStruct = new VertexStructure();
|
||||
instStruct.add("ipos", VertexData.Float3);
|
||||
if (instancingType == 2 || instancingType == 4) {
|
||||
instStruct.add("irot", VertexData.Float3);
|
||||
}
|
||||
if (instancingType == 3 || instancingType == 4) {
|
||||
instStruct.add("iscl", VertexData.Float3);
|
||||
}
|
||||
instStruct.instanced = true;
|
||||
pipeState.inputLayout = [structure, instStruct];
|
||||
}
|
||||
else { // Regular
|
||||
pipeState.inputLayout = [structure];
|
||||
}
|
||||
|
||||
// Depth
|
||||
pipeState.depthWrite = raw.depth_write;
|
||||
pipeState.depthMode = getCompareMode(raw.compare_mode);
|
||||
|
||||
// Cull
|
||||
pipeState.cullMode = getCullMode(raw.cull_mode);
|
||||
|
||||
// Blending
|
||||
if (raw.blend_source != null) pipeState.blendSource = getBlendingFactor(raw.blend_source);
|
||||
if (raw.blend_destination != null) pipeState.blendDestination = getBlendingFactor(raw.blend_destination);
|
||||
if (raw.blend_operation != null) pipeState.blendOperation = getBlendingOperation(raw.blend_operation);
|
||||
if (raw.alpha_blend_source != null) pipeState.alphaBlendSource = getBlendingFactor(raw.alpha_blend_source);
|
||||
if (raw.alpha_blend_destination != null) pipeState.alphaBlendDestination = getBlendingFactor(raw.alpha_blend_destination);
|
||||
if (raw.alpha_blend_operation != null) pipeState.alphaBlendOperation = getBlendingOperation(raw.alpha_blend_operation);
|
||||
|
||||
// Per-target color write mask
|
||||
if (raw.color_writes_red != null) for (i in 0...raw.color_writes_red.length) pipeState.colorWriteMasksRed[i] = raw.color_writes_red[i];
|
||||
if (raw.color_writes_green != null) for (i in 0...raw.color_writes_green.length) pipeState.colorWriteMasksGreen[i] = raw.color_writes_green[i];
|
||||
if (raw.color_writes_blue != null) for (i in 0...raw.color_writes_blue.length) pipeState.colorWriteMasksBlue[i] = raw.color_writes_blue[i];
|
||||
if (raw.color_writes_alpha != null) for (i in 0...raw.color_writes_alpha.length) pipeState.colorWriteMasksAlpha[i] = raw.color_writes_alpha[i];
|
||||
|
||||
// Color attachment format
|
||||
if (raw.color_attachments != null) {
|
||||
pipeState.colorAttachmentCount = raw.color_attachments.length;
|
||||
for (i in 0...raw.color_attachments.length) pipeState.colorAttachments[i] = getTextureFormat(raw.color_attachments[i]);
|
||||
}
|
||||
|
||||
// Depth attachment format
|
||||
if (raw.depth_attachment != null) {
|
||||
#if (krom_windows || krom_linux || krom_darwin || krom_android || krom_ios)
|
||||
pipeState.depthStencilAttachment = getDepthStencilFormat(raw.depth_attachment);
|
||||
#end
|
||||
}
|
||||
|
||||
// Conservative raster for voxelization
|
||||
if (raw.conservative_raster != null) pipeState.conservativeRasterization = raw.conservative_raster;
|
||||
|
||||
// Shaders
|
||||
if (raw.shader_from_source) {
|
||||
pipeState.vertexShader = VertexShader.fromSource(raw.vertex_shader);
|
||||
pipeState.fragmentShader = FragmentShader.fromSource(raw.fragment_shader);
|
||||
|
||||
#if kha_krom
|
||||
// Shader compile error
|
||||
if (pipeState.vertexShader.shader == null || pipeState.fragmentShader.shader == null) {
|
||||
done(null);
|
||||
return;
|
||||
}
|
||||
#end
|
||||
|
||||
finishCompile(done);
|
||||
}
|
||||
else {
|
||||
|
||||
#if (lnx_noembed && kha_krom) // Load shaders manually
|
||||
|
||||
var shadersLoaded = 0;
|
||||
var numShaders = 2;
|
||||
if (raw.geometry_shader != null) numShaders++;
|
||||
if (raw.tesscontrol_shader != null) numShaders++;
|
||||
if (raw.tesseval_shader != null) numShaders++;
|
||||
|
||||
function loadShader(file: String, type: Int) {
|
||||
var path = ShaderData.shaderPath + file + ShaderData.shaderExt;
|
||||
Data.getBlob(path, function(b: kha.Blob) {
|
||||
if (type == 0) pipeState.vertexShader = new VertexShader([b], [file]);
|
||||
else if (type == 1) pipeState.fragmentShader = new FragmentShader([b], [file]);
|
||||
else if (type == 2) pipeState.geometryShader = new kha.graphics4.GeometryShader([b], [file]);
|
||||
else if (type == 3) pipeState.tessellationControlShader = new kha.graphics4.TessellationControlShader([b], [file]);
|
||||
else if (type == 4) pipeState.tessellationEvaluationShader = new kha.graphics4.TessellationEvaluationShader([b], [file]);
|
||||
shadersLoaded++;
|
||||
if (shadersLoaded >= numShaders) finishCompile(done);
|
||||
});
|
||||
}
|
||||
loadShader(raw.vertex_shader, 0);
|
||||
loadShader(raw.fragment_shader, 1);
|
||||
if (raw.geometry_shader != null) loadShader(raw.geometry_shader, 2);
|
||||
if (raw.tesscontrol_shader != null) loadShader(raw.tesscontrol_shader, 3);
|
||||
if (raw.tesseval_shader != null) loadShader(raw.tesseval_shader, 4);
|
||||
|
||||
#elseif lnx_shader_embed
|
||||
|
||||
pipeState.fragmentShader = kha.Shaders.getFragment(raw.fragment_shader);
|
||||
pipeState.vertexShader = kha.Shaders.getVertex(raw.vertex_shader);
|
||||
if (raw.geometry_shader != null) {
|
||||
pipeState.geometryShader = kha.Shaders.getGeometry(raw.geometry_shader);
|
||||
}
|
||||
finishCompile(done);
|
||||
|
||||
#else
|
||||
|
||||
pipeState.fragmentShader = Reflect.field(kha.Shaders, raw.fragment_shader.replace(".", "_"));
|
||||
pipeState.vertexShader = Reflect.field(kha.Shaders, raw.vertex_shader.replace(".", "_"));
|
||||
|
||||
if (raw.geometry_shader != null) {
|
||||
pipeState.geometryShader = Reflect.field(kha.Shaders, raw.geometry_shader.replace(".", "_"));
|
||||
}
|
||||
if (raw.tesscontrol_shader != null) {
|
||||
pipeState.tessellationControlShader = Reflect.field(kha.Shaders, raw.tesscontrol_shader.replace(".", "_"));
|
||||
}
|
||||
if (raw.tesseval_shader != null) {
|
||||
pipeState.tessellationEvaluationShader = Reflect.field(kha.Shaders, raw.tesseval_shader.replace(".", "_"));
|
||||
}
|
||||
finishCompile(done);
|
||||
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
function finishCompile(done: ShaderContext->Void) {
|
||||
// Override specified values
|
||||
if (overrideContext != null) {
|
||||
if (overrideContext.cull_mode != null) {
|
||||
pipeState.cullMode = getCullMode(overrideContext.cull_mode);
|
||||
}
|
||||
}
|
||||
|
||||
pipeState.compile();
|
||||
|
||||
if (raw.constants != null) {
|
||||
for (c in raw.constants) addConstant(c);
|
||||
}
|
||||
|
||||
if (raw.texture_units != null) {
|
||||
for (tu in raw.texture_units) addTexture(tu);
|
||||
}
|
||||
|
||||
done(this);
|
||||
}
|
||||
|
||||
public static function parseData(data: String): VertexData {
|
||||
if (data == "float1") return VertexData.Float1;
|
||||
else if (data == "float2") return VertexData.Float2;
|
||||
else if (data == "float3") return VertexData.Float3;
|
||||
else if (data == "float4") return VertexData.Float4;
|
||||
else if (data == "short2norm") return VertexData.Short2Norm;
|
||||
else if (data == "short4norm") return VertexData.Short4Norm;
|
||||
return VertexData.Float1;
|
||||
}
|
||||
|
||||
function parseVertexStructure() {
|
||||
structure = new VertexStructure();
|
||||
var ipos = false;
|
||||
var irot = false;
|
||||
var iscl = false;
|
||||
for (elem in raw.vertex_elements) {
|
||||
#if cpp
|
||||
if (Reflect.field(elem, "name") == "ipos") { ipos = true; continue; }
|
||||
if (Reflect.field(elem, "name") == "irot") { irot = true; continue; }
|
||||
if (Reflect.field(elem, "name") == "iscl") { iscl = true; continue; }
|
||||
#else
|
||||
if (elem.name == "ipos") { ipos = true; continue; }
|
||||
if (elem.name == "irot") { irot = true; continue; }
|
||||
if (elem.name == "iscl") { iscl = true; continue; }
|
||||
#end
|
||||
structure.add(elem.name, parseData(elem.data));
|
||||
}
|
||||
if (ipos && !irot && !iscl) instancingType = 1;
|
||||
else if (ipos && irot && !iscl) instancingType = 2;
|
||||
else if (ipos && !irot && iscl) instancingType = 3;
|
||||
else if (ipos && irot && iscl) instancingType = 4;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
if (pipeState.fragmentShader != null) pipeState.fragmentShader.delete();
|
||||
if (pipeState.vertexShader != null) pipeState.vertexShader.delete();
|
||||
if (pipeState.geometryShader != null) pipeState.geometryShader.delete();
|
||||
if (pipeState.tessellationControlShader != null) pipeState.tessellationControlShader.delete();
|
||||
if (pipeState.tessellationEvaluationShader != null) pipeState.tessellationEvaluationShader.delete();
|
||||
pipeState.delete();
|
||||
}
|
||||
|
||||
function getCompareMode(s: String): CompareMode {
|
||||
switch (s) {
|
||||
case "always": return CompareMode.Always;
|
||||
case "never": return CompareMode.Never;
|
||||
case "less": return CompareMode.Less;
|
||||
case "less_equal": return CompareMode.LessEqual;
|
||||
case "greater": return CompareMode.Greater;
|
||||
case "greater_equal": return CompareMode.GreaterEqual;
|
||||
case "equal": return CompareMode.Equal;
|
||||
case "not_equal": return CompareMode.NotEqual;
|
||||
default: return CompareMode.Less;
|
||||
}
|
||||
}
|
||||
|
||||
function getCullMode(s: String): CullMode {
|
||||
switch (s) {
|
||||
case "none": return CullMode.None;
|
||||
case "clockwise": return CullMode.Clockwise;
|
||||
default: return CullMode.CounterClockwise;
|
||||
}
|
||||
}
|
||||
|
||||
function getBlendingOperation(s: String): BlendingOperation {
|
||||
switch (s) {
|
||||
case "add": return BlendingOperation.Add;
|
||||
case "subtract": return BlendingOperation.Subtract;
|
||||
case "reverse_subtract": return BlendingOperation.ReverseSubtract;
|
||||
case "min": return BlendingOperation.Min;
|
||||
case "max": return BlendingOperation.Max;
|
||||
default: return BlendingOperation.Add;
|
||||
}
|
||||
}
|
||||
|
||||
function getBlendingFactor(s: String): BlendingFactor {
|
||||
switch (s) {
|
||||
case "blend_one": return BlendingFactor.BlendOne;
|
||||
case "blend_zero": return BlendingFactor.BlendZero;
|
||||
case "source_alpha": return BlendingFactor.SourceAlpha;
|
||||
case "destination_alpha": return BlendingFactor.DestinationAlpha;
|
||||
case "inverse_source_alpha": return BlendingFactor.InverseSourceAlpha;
|
||||
case "inverse_destination_alpha": return BlendingFactor.InverseDestinationAlpha;
|
||||
case "source_color": return BlendingFactor.SourceColor;
|
||||
case "destination_color": return BlendingFactor.DestinationColor;
|
||||
case "inverse_source_color": return BlendingFactor.InverseSourceColor;
|
||||
case "inverse_destination_color": return BlendingFactor.InverseDestinationColor;
|
||||
default: return BlendingFactor.Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getTextureAddresing(s: String): TextureAddressing {
|
||||
switch (s) {
|
||||
case "repeat": return TextureAddressing.Repeat;
|
||||
case "mirror": return TextureAddressing.Mirror;
|
||||
default: return TextureAddressing.Clamp;
|
||||
}
|
||||
}
|
||||
|
||||
function getTextureFilter(s: String): TextureFilter {
|
||||
switch (s) {
|
||||
case "point": return TextureFilter.PointFilter;
|
||||
case "linear": return TextureFilter.LinearFilter;
|
||||
default: return TextureFilter.AnisotropicFilter;
|
||||
}
|
||||
}
|
||||
|
||||
function getMipmapFilter(s: String): MipMapFilter {
|
||||
switch (s) {
|
||||
case "no": return MipMapFilter.NoMipFilter;
|
||||
case "point": return MipMapFilter.PointMipFilter;
|
||||
default: return MipMapFilter.LinearMipFilter;
|
||||
}
|
||||
}
|
||||
|
||||
function getTextureFormat(s: String): TextureFormat {
|
||||
switch (s) {
|
||||
case "RGBA32": return TextureFormat.RGBA32;
|
||||
case "RGBA64": return TextureFormat.RGBA64;
|
||||
case "RGBA128": return TextureFormat.RGBA128;
|
||||
case "DEPTH16": return TextureFormat.DEPTH16;
|
||||
case "R32": return TextureFormat.A32;
|
||||
case "R16": return TextureFormat.A16;
|
||||
case "R8": return TextureFormat.L8;
|
||||
default: return TextureFormat.RGBA32;
|
||||
}
|
||||
}
|
||||
|
||||
function getDepthStencilFormat(s: String): DepthStencilFormat {
|
||||
switch (s) {
|
||||
case "DEPTH32": return DepthStencilFormat.DepthOnly;
|
||||
case "NONE": return DepthStencilFormat.NoDepthAndStencil;
|
||||
default: return DepthStencilFormat.DepthOnly;
|
||||
}
|
||||
}
|
||||
|
||||
function addConstant(c: TShaderConstant) {
|
||||
constants.push(pipeState.getConstantLocation(c.name));
|
||||
}
|
||||
|
||||
function addTexture(tu: TTextureUnit) {
|
||||
var unit = pipeState.getTextureUnit(tu.name);
|
||||
textureUnits.push(unit);
|
||||
}
|
||||
|
||||
public function setTextureParameters(g: kha.graphics4.Graphics, unitIndex: Int, tex: TBindTexture) {
|
||||
// This function is called for samplers set using material context
|
||||
var unit = textureUnits[unitIndex];
|
||||
g.setTextureParameters(unit,
|
||||
tex.u_addressing == null ? TextureAddressing.Repeat : getTextureAddresing(tex.u_addressing),
|
||||
tex.v_addressing == null ? TextureAddressing.Repeat : getTextureAddresing(tex.v_addressing),
|
||||
tex.min_filter == null ? TextureFilter.LinearFilter : getTextureFilter(tex.min_filter),
|
||||
tex.mag_filter == null ? TextureFilter.LinearFilter : getTextureFilter(tex.mag_filter),
|
||||
tex.mipmap_filter == null ? MipMapFilter.NoMipFilter : getMipmapFilter(tex.mipmap_filter));
|
||||
}
|
||||
}
|
157
leenkx/Sources/iron/data/TerrainStream.hx
Normal file
157
leenkx/Sources/iron/data/TerrainStream.hx
Normal file
@ -0,0 +1,157 @@
|
||||
package iron.data;
|
||||
|
||||
import haxe.ds.Vector;
|
||||
import kha.arrays.Int16Array;
|
||||
import kha.arrays.Uint32Array;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.object.Object;
|
||||
import iron.object.CameraObject;
|
||||
import iron.object.MeshObject;
|
||||
import iron.object.Uniforms;
|
||||
import iron.Scene;
|
||||
|
||||
#if lnx_terrain
|
||||
|
||||
class TerrainStream {
|
||||
|
||||
public var sectors: Array<MeshObject> = [];
|
||||
public var heightTextures: Array<kha.Image> = [];
|
||||
public var ready = false;
|
||||
public var onReady: Void->Void = null;
|
||||
|
||||
var raw: TTerrainData;
|
||||
var planes: Array<MeshData> = [];
|
||||
var materials: Vector<MaterialData>;
|
||||
|
||||
public function new(raw: TTerrainData) {
|
||||
this.raw = raw;
|
||||
|
||||
Data.getMaterial(Scene.active.raw.name, raw.material_ref, function(mat: MaterialData) {
|
||||
materials = Vector.fromData([mat]);
|
||||
|
||||
var imagesLoaded = 0;
|
||||
var numSectors = raw.sectors_x * raw.sectors_y;
|
||||
for (i in 0...numSectors) {
|
||||
var j = i + 1;
|
||||
var ext = j < 10 ? "0" + j : "" + j;
|
||||
Data.getImage("heightmap_" + ext + ".png", function(image: kha.Image) {
|
||||
heightTextures[i] = image;
|
||||
imagesLoaded++;
|
||||
if (imagesLoaded == numSectors) {
|
||||
loaded();
|
||||
}
|
||||
}, true); // Readable
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function notifyOnReady(f: Void->Void) {
|
||||
onReady = f;
|
||||
if (ready) onReady();
|
||||
}
|
||||
|
||||
function loaded() {
|
||||
for (i in 0...4) {
|
||||
makePlane(i, raw.sector_size, raw.sector_size, heightTextures[0].width, heightTextures[0].height);
|
||||
}
|
||||
|
||||
for (i in 0...raw.sectors_x * raw.sectors_y) {
|
||||
makeSector(i);
|
||||
}
|
||||
|
||||
iron.App.notifyOnInit(function() {
|
||||
Uniforms.externalTextureLinks.push(textureLink);
|
||||
});
|
||||
|
||||
ready = true;
|
||||
if (onReady != null) onReady();
|
||||
}
|
||||
|
||||
function makePlane(index: Int, sizeX: Float, sizeY: Float, vertsX: Int, vertsY: Int) {
|
||||
// Pack positions to (-1, 1) range
|
||||
var halfX = sizeX / 2;
|
||||
var halfY = sizeY / 2;
|
||||
var halfZ = raw.height_scale / 2;
|
||||
var scalePos = Math.max(halfX, Math.max(halfY, halfZ));
|
||||
var inv = 1 / scalePos;
|
||||
|
||||
var posa = new Int16Array(vertsX * vertsY * 4);
|
||||
var nora = new Int16Array(vertsX * vertsY * 2);
|
||||
var texa = new Int16Array(vertsX * vertsY * 2);
|
||||
var inda = new Uint32Array((vertsX - 1) * (vertsY - 1) * 6);
|
||||
var stepX = sizeX / (vertsX - 1);
|
||||
var stepY = sizeY / (vertsY - 1);
|
||||
for (i in 0...vertsX * vertsY) {
|
||||
var x = (i % vertsX) * stepX - halfX;
|
||||
var y = Std.int(i / vertsX) * stepY - halfY;
|
||||
posa[i * 4 ] = Std.int(x * 32767 * inv);
|
||||
posa[i * 4 + 1] = Std.int(y * 32767 * inv);
|
||||
posa[i * 4 + 2] = Std.int(-halfZ * 32767 * inv);
|
||||
nora[i * 2 ] = 0;
|
||||
nora[i * 2 + 1] = 0;
|
||||
posa[i * 4 + 3] = 32767;
|
||||
x = (i % vertsX) / vertsX;
|
||||
y = (Std.int(i / vertsX)) / vertsY;
|
||||
texa[i * 2 ] = Std.int(x * 32767);
|
||||
texa[i * 2 + 1] = Std.int(y * 32767);
|
||||
}
|
||||
for (i in 0...(vertsX - 1) * (vertsY - 1)) {
|
||||
var x = i % (vertsX - 1);
|
||||
var y = Std.int(i / (vertsY - 1));
|
||||
inda[i * 6 ] = y * vertsX + x;
|
||||
inda[i * 6 + 1] = y * vertsX + x + 1;
|
||||
inda[i * 6 + 2] = (y + 1) * vertsX + x;
|
||||
inda[i * 6 + 3] = y * vertsX + x + 1;
|
||||
inda[i * 6 + 4] = (y + 1) * vertsX + x + 1;
|
||||
inda[i * 6 + 5] = (y + 1) * vertsX + x;
|
||||
}
|
||||
|
||||
// Positions, normals and indices
|
||||
var pos: TVertexArray = { attrib: "pos", values: posa, data: "short4norm" };
|
||||
var nor: TVertexArray = { attrib: "nor", values: nora, data: "short2norm" };
|
||||
var tex: TVertexArray = { attrib: "tex", values: texa, data: "short2norm" };
|
||||
var ind: TIndexArray = { material: 0, values: inda };
|
||||
|
||||
var rawmeshData: TMeshData = {
|
||||
name: "Terrain",
|
||||
vertex_arrays: [pos, nor, tex],
|
||||
index_arrays: [ind],
|
||||
scale_pos: scalePos,
|
||||
scale_tex: 1.0
|
||||
};
|
||||
|
||||
new MeshData(rawmeshData, function(data: MeshData) {
|
||||
planes[index] = data;
|
||||
data.geom.calculateAABB();
|
||||
});
|
||||
}
|
||||
|
||||
function makeSector(index: Int) {
|
||||
var object = Scene.active.addMeshObject(planes[0], materials);
|
||||
sectors[index] = object;
|
||||
object.uid = index;
|
||||
object.name = "Terrain." + index;
|
||||
object.transform.loc.x = (index % raw.sectors_x) * 2;
|
||||
object.transform.loc.y = Std.int(index / raw.sectors_x) * 2;
|
||||
object.transform.loc.z = 0;
|
||||
object.transform.buildMatrix();
|
||||
object.transform.dim.x = raw.sector_size;
|
||||
object.transform.dim.y = raw.sector_size;
|
||||
object.transform.dim.z = raw.height_scale;
|
||||
}
|
||||
|
||||
public function remove() {}
|
||||
|
||||
public function update(camera: CameraObject) {
|
||||
if (!ready) return;
|
||||
}
|
||||
|
||||
function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
|
||||
if (link == "_TerrainHeight") {
|
||||
return heightTextures[object.uid];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
52
leenkx/Sources/iron/data/Wasm.hx
Normal file
52
leenkx/Sources/iron/data/Wasm.hx
Normal file
@ -0,0 +1,52 @@
|
||||
package iron.data;
|
||||
|
||||
#if js
|
||||
|
||||
class Wasm {
|
||||
|
||||
public var exports: Dynamic;
|
||||
|
||||
public static inline function instance(blob: kha.Blob, importObject: Dynamic = null): Wasm {
|
||||
var data = blob.toBytes().getData();
|
||||
var module = new js.lib.webassembly.Module(data);
|
||||
var exports: Dynamic = importObject == null ?
|
||||
new js.lib.webassembly.Instance(module).exports :
|
||||
new js.lib.webassembly.Instance(module, importObject).exports;
|
||||
return new Wasm(exports);
|
||||
}
|
||||
|
||||
#if kha_html5_js
|
||||
public static function instantiateStreaming(blob: kha.Blob, importObject: Dynamic = null, done: Wasm->Void) {
|
||||
js.lib.WebAssembly.instantiateStreaming(new js.html.Response(blob.toBytes().getData(), {
|
||||
headers: new js.html.Headers({"Content-Type": "application/wasm"})
|
||||
}), importObject).then(m -> done(new Wasm(m.instance.exports)));
|
||||
}
|
||||
#end
|
||||
|
||||
function new(exports: Dynamic) {
|
||||
this.exports = exports;
|
||||
}
|
||||
|
||||
public function getString(i: Int): String { // Retrieve string from memory pointer
|
||||
var mem = getMemory(i, 32);
|
||||
var s = "";
|
||||
for (i in 0...32) {
|
||||
mem[i] == 0 ? break : s += String.fromCharCode(mem[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public inline function getMemory(offset: Int, length: Int): js.lib.Uint8Array {
|
||||
return new js.lib.Uint8Array(exports.memory.buffer, offset, length);
|
||||
}
|
||||
|
||||
public inline function getMemoryF32(offset: Int, length: Int): kha.arrays.Float32Array {
|
||||
return new kha.arrays.Float32Array(exports.memory.buffer).subarray( offset, length );
|
||||
}
|
||||
|
||||
public inline function getMemoryU32(offset: Int, length: Int): kha.arrays.Uint32Array {
|
||||
return new kha.arrays.Uint32Array(exports.memory.buffer).subarray(offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
128
leenkx/Sources/iron/data/WorldData.hx
Normal file
128
leenkx/Sources/iron/data/WorldData.hx
Normal file
@ -0,0 +1,128 @@
|
||||
package iron.data;
|
||||
|
||||
import haxe.Json;
|
||||
import kha.arrays.Float32Array;
|
||||
import iron.math.Vec4;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.system.LnxPack;
|
||||
using StringTools;
|
||||
|
||||
class WorldData {
|
||||
|
||||
public var name: String;
|
||||
public var raw: TWorldData;
|
||||
public var envmap: kha.Image;
|
||||
public var probe: Probe;
|
||||
|
||||
static var emptyIrr: Float32Array = null;
|
||||
|
||||
public function new(raw: TWorldData, done: WorldData->Void) {
|
||||
this.raw = raw;
|
||||
this.name = raw.name;
|
||||
|
||||
// Parse probes
|
||||
if (raw.probe != null) {
|
||||
new Probe(raw.probe, function(self: Probe) {
|
||||
probe = self;
|
||||
#if lnx_skip_envmap
|
||||
done(this);
|
||||
#else
|
||||
loadEnvmap(done);
|
||||
#end
|
||||
});
|
||||
}
|
||||
else {
|
||||
#if lnx_skip_envmap
|
||||
done(this);
|
||||
#else
|
||||
loadEnvmap(done);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
public function loadEnvmap(done: WorldData->Void) {
|
||||
if (raw.envmap != null) {
|
||||
Data.getImage(raw.envmap, function(image: kha.Image) {
|
||||
envmap = image;
|
||||
done(this);
|
||||
});
|
||||
}
|
||||
else done(this);
|
||||
}
|
||||
|
||||
public static function parse(name: String, id: String, done: WorldData->Void) {
|
||||
Data.getSceneRaw(name, function(format: TSceneFormat) {
|
||||
var raw: TWorldData = Data.getWorldRawByName(format.world_datas, id);
|
||||
if (raw == null) {
|
||||
trace('World data "$id" not found!');
|
||||
done(null);
|
||||
}
|
||||
new WorldData(raw, done);
|
||||
});
|
||||
}
|
||||
|
||||
public static function getEmptyIrradiance(): Float32Array {
|
||||
if (emptyIrr == null) {
|
||||
emptyIrr = new Float32Array(28);
|
||||
for (i in 0...emptyIrr.length) emptyIrr.set(i, 0.0);
|
||||
}
|
||||
return emptyIrr;
|
||||
}
|
||||
}
|
||||
|
||||
class Probe {
|
||||
|
||||
public var raw: TProbeData;
|
||||
public var radiance: kha.Image;
|
||||
public var radianceMipmaps: Array<kha.Image> = [];
|
||||
public var irradiance: Float32Array;
|
||||
|
||||
public function new(raw: TProbeData, done: Probe->Void) {
|
||||
this.raw = raw;
|
||||
|
||||
setIrradiance(function(irr: Float32Array) {
|
||||
irradiance = irr;
|
||||
if (raw.radiance != null) {
|
||||
Data.getImage(raw.radiance, function(rad: kha.Image) {
|
||||
radiance = rad;
|
||||
while (radianceMipmaps.length < raw.radiance_mipmaps) radianceMipmaps.push(null);
|
||||
var dot = raw.radiance.lastIndexOf(".");
|
||||
var ext = raw.radiance.substring(dot);
|
||||
var base = raw.radiance.substring(0, dot);
|
||||
|
||||
var mipsLoaded = 0;
|
||||
for (i in 0...raw.radiance_mipmaps) {
|
||||
Data.getImage(base + "_" + i + ext, function(mipimg: kha.Image) {
|
||||
radianceMipmaps[i] = mipimg;
|
||||
mipsLoaded++;
|
||||
|
||||
if (mipsLoaded == raw.radiance_mipmaps) {
|
||||
radiance.setMipmaps(radianceMipmaps);
|
||||
done(this);
|
||||
}
|
||||
}, true); // Readable
|
||||
}
|
||||
});
|
||||
}
|
||||
else done(this);
|
||||
});
|
||||
}
|
||||
|
||||
function setIrradiance(done: Float32Array->Void) {
|
||||
// Parse probe data
|
||||
if (raw.irradiance == null) {
|
||||
done(WorldData.getEmptyIrradiance());
|
||||
}
|
||||
else {
|
||||
var ext = raw.irradiance.endsWith(".json") ? "" : ".lnx";
|
||||
Data.getBlob(raw.irradiance + ext, function(b: kha.Blob) {
|
||||
var irradianceParsed: TSceneFormat = ext == "" ?
|
||||
Json.parse(b.toString()) :
|
||||
LnxPack.decode(b.toBytes());
|
||||
var irr = new Float32Array(28); // Align to mult of 4 - 27->28
|
||||
for (i in 0...27) irr[i] = irradianceParsed.irradiance[i];
|
||||
done(irr);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user