package src; import h3d.scene.MultiMaterial; import shaders.DefaultCubemapMaterial; import shaders.DefaultNormalMaterial; import shaders.DefaultMaterial; import h3d.scene.Mesh; import shaders.NormalMaterial; import shaders.NoiseTileMaterial; import shaders.DirLight; import shaders.PhongMaterial; import dif.Edge; import h3d.shader.pbr.PropsValues; import h3d.mat.Material; import src.ResourceLoader; import src.PathedInterior; import h3d.Vector; import collision.CollisionSurface; import collision.CollisionEntity; import h3d.mat.Data.Wrap; import hxd.fs.FileSystem; import hxd.res.Loader; import hxd.res.Image; import h3d.mat.Texture; import haxe.io.Path; import hxd.File; import mesh.Polygon; import h3d.prim.UV; import h3d.col.Point; import dif.math.Point2F; import dif.math.Point3F; import h3d.prim.BigPrimitive; import dif.Interior; import dif.Dif; import src.InteriorObject; import src.MarbleGame; import src.ResourceLoaderWorker; import src.Console; import src.Debug; import src.LRUCache; class DifBuilderTriangle { public var texture:String; public var normal1:Point3F; public var normal2:Point3F; public var normal3:Point3F; public var p1:Point3F; public var p2:Point3F; public var p3:Point3F; public var uv1:Point2F; public var uv2:Point2F; public var uv3:Point2F; public var t1:Point3F; public var t2:Point3F; public var t3:Point3F; public var b1:Point3F; public var b2:Point3F; public var b3:Point3F; public var n1:Point3F; public var n2:Point3F; public var n3:Point3F; public function new() {} } class TriangleEdge { public var index1:Int; public var index2:Int; public var farPoint:Int; public var surfaceIndex:Int; public function new(i1:Int, i2:Int, farPoint:Int, surfaceIndex:Int) { if (i2 < i1) { index1 = i2; index2 = i1; } else { index1 = i1; index2 = i2; } this.farPoint = farPoint; this.surfaceIndex = surfaceIndex; } } @:publicFields class DifCache { var dif:Dif; var difTriangles:Map>; var prim:Polygon; public function new() {} } typedef VertexBucket = { var referenceNormal:Point3F; var triangleIndices:Array; var normals:Array; } class DifBuilder { static var difCache:LRUCache = new LRUCache(24); static var materialDict:Map = [ "friction_none" => { friction: 0.01, restitution: 0.5 }, "friction_low" => { friction: 0.2, restitution: 0.5 }, "friction_low_shadow" => { friction: 0.2, restitution: 0.5 }, "friction_high" => { friction: 1.5, restitution: 0.5 }, "friction_ramp_yellow" => { friction: 2.0, restitution: 1.0 }, "oilslick" => { friction: 0.05, restitution: 0.5 }, "base.slick" => { friction: 0.05, restitution: 0.5 }, "ice.slick" => { friction: 0.05, restitution: 0.5 }, "grass" => { friction: 1.5, restitution: 0.35 }, "ice1" => { friction: 0.03, restitution: 0.95 }, "rug" => { friction: 6.0, restitution: 0.5, }, "tarmac" => { friction: 0.35, restitution: 0.7 }, "carpet" => { friction: 6.0, restitution: 0.5 }, "sand" => { friction: 4.0, restitution: 0.1 }, "water" => { friction: 6.0, restitution: 0.0, }, "floor_bounce" => { friction: 0.2, restitution: 0.0, force: 15 }, "mbp_chevron_friction" => { friction: -1.0, restitution: 1.0 }, "mbp_chevron_friction2" => { friction: -1.0, restitution: 1.0 }, "mbp_chevron_friction3" => { friction: -1.0, restitution: 1.0 }, "mmg_grass" => { friction: 0.9, restitution: 0.5 }, "mmg_sand" => { friction: 6.0, restitution: 0.1 }, "mmg_water" => { friction: 6.0, restitution: 0.0 }, "mmg_ice" => { friction: 0.03, restitution: 0.95 }, "mmg_ice_shadow" => { friction: 0.03, restitution: 0.95 }, "friction_mp_high" => { friction: 6, restitution: 0.3 }, "friction_mp_high_shadow" => { friction: 6, restitution: 0.3 }, "friction_high_shadow" => { friction: 6, restitution: 0.3 }, ]; static function createDefaultMaterial(onFinish:hxsl.Shader->Void, baseTexture:String, normalTexture:String, shininess:Float, specularColor:Vector, uvScaleFactor:Float = 1, half:Bool = false) { var worker = new ResourceLoaderWorker(() -> { var diffuseTex = ResourceLoader.getTexture(baseTexture).resource; diffuseTex.wrap = Repeat; diffuseTex.mipMap = Nearest; // diffuseTex.filter = Linear; var normalTex = ResourceLoader.getTexture(normalTexture).resource; normalTex.wrap = Repeat; normalTex.mipMap = Nearest; var shader = new DefaultMaterial(diffuseTex, normalTex, shininess, specularColor, uvScaleFactor, half); onFinish(shader); }); worker.loadFile(baseTexture); worker.loadFile(normalTexture); worker.run(); } static function createDefaultCubemapMaterial(onFinish:hxsl.Shader->Void, baseTexture:String, normalTexture:String, shininess:Float, specularColor:Vector, uvScaleFactor:Float = 1) { var worker = new ResourceLoaderWorker(() -> { var diffuseTex = ResourceLoader.getTexture(baseTexture).resource; diffuseTex.wrap = Repeat; diffuseTex.mipMap = Linear; diffuseTex.filter = Linear; var normalTex = ResourceLoader.getTexture(normalTexture).resource; normalTex.wrap = Repeat; normalTex.mipMap = Nearest; var cubemapTex = new h3d.mat.Texture(128, 128, [Cube]); var cubemapFace1 = ResourceLoader.getImage('data/textures/acubexpos2.png').resource; var cubemapFace2 = ResourceLoader.getImage('data/textures/acubexneg2.png').resource; var cubemapFace3 = ResourceLoader.getImage('data/textures/acubezneg2.png').resource; var cubemapFace4 = ResourceLoader.getImage('data/textures/acubezpos2.png').resource; var cubemapFace5 = ResourceLoader.getImage('data/textures/acubeypos2.png').resource; var cubemapFace6 = ResourceLoader.getImage('data/textures/acubeyneg2.png').resource; cubemapTex.uploadPixels(cubemapFace1.getPixels(), 0, 0); cubemapTex.uploadPixels(cubemapFace2.getPixels(), 0, 1); cubemapTex.uploadPixels(cubemapFace3.getPixels(), 0, 2); cubemapTex.uploadPixels(cubemapFace4.getPixels(), 0, 3); cubemapTex.uploadPixels(cubemapFace5.getPixels(), 0, 4); cubemapTex.uploadPixels(cubemapFace6.getPixels(), 0, 5); var shader = new DefaultCubemapMaterial(diffuseTex, normalTex, shininess, specularColor, uvScaleFactor, cubemapTex); onFinish(shader); }); worker.loadFile(baseTexture); worker.loadFile(normalTexture); worker.run(); } static function createDefaultNormalMaterial(onFinish:hxsl.Shader->Void, baseTexture:String, shininess:Float, specularColor:Vector, uvScaleFactor:Float = 1) { var worker = new ResourceLoaderWorker(() -> { var diffuseTex = ResourceLoader.getTexture(baseTexture).resource; diffuseTex.wrap = Repeat; diffuseTex.mipMap = Linear; diffuseTex.filter = Linear; var shader = new DefaultNormalMaterial(diffuseTex, shininess, specularColor, uvScaleFactor); onFinish(shader); }); worker.loadFile(baseTexture); worker.run(); } static function createNoiseTileMaterial(onFinish:hxsl.Shader->Void, baseTexture:String, noiseSuffix:String, shininess:Float, specular:Vector, uvScale:Float = 1.0) { var worker = new ResourceLoaderWorker(() -> { var diffuseTex = ResourceLoader.getTexture('data/textures/${baseTexture}').resource; diffuseTex.wrap = Repeat; diffuseTex.mipMap = Linear; diffuseTex.filter = Linear; var normalTex = ResourceLoader.getTexture('data/textures/tile_intermediate.normal.png').resource; normalTex.wrap = Repeat; normalTex.mipMap = Nearest; var noiseTex = ResourceLoader.getTexture('data/textures/noise${noiseSuffix}.jpg').resource; noiseTex.wrap = Repeat; noiseTex.mipMap = None; var shader = new NoiseTileMaterial(diffuseTex, normalTex, noiseTex, shininess, specular, uvScale); #if hl shader.useAccurateNoise = true; #end onFinish(shader); }); worker.loadFile('textures/${baseTexture}'); worker.loadFile('textures/noise${noiseSuffix}.jpg'); worker.loadFile('textures/tile_intermediate.normal.png'); worker.run(); } static function createNormalMapMaterial(onFinish:hxsl.Shader->Void, baseTexture:String, normalTexture:String) { var worker = new ResourceLoaderWorker(() -> { var diffuseTex = ResourceLoader.getTexture('data/interiors_mbu/${baseTexture}').resource; var normalTex = ResourceLoader.getTexture('data/shaders/tex/${normalTexture}').resource; normalTex.wrap = Repeat; var shader = new NormalMaterial(diffuseTex, normalTex, MarbleGame.instance.world.ambient, MarbleGame.instance.world.dirLight, MarbleGame.instance.world.dirLightDir); onFinish(shader); }); worker.loadFile('interiors_mbu/${baseTexture}'); worker.loadFile('shaders/tex/${normalTexture}'); worker.run(); } static var shaderMaterialDict:MapVoid)->Void> = [ 'plate_1' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/plate.randomize.png', 'data/textures/plate.normal.png', 8, new Vector(1, 1, 0.8, 1), 1, true), 'plate_1_small' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/plate.randomize.png', 'data/textures/plate.normal.png', 8, new Vector(1, 1, 0.8, 1), 1, false), // Tiles 'tile_beginner' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '', 40, new Vector(1, 1, 1, 1)), 'tile_beginner_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_beginner_red' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red', 40, new Vector(1, 1, 1, 1)), 'tile_beginner_red_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_beginner_blue' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue', 40, new Vector(1, 1, 1, 1)), 'tile_beginner_blue_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_intermediate' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '', 40, new Vector(1, 1, 1, 1)), 'tile_intermediate_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_intermediate_red' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, new Vector(1, 1, 1, 1)), 'tile_intermediate_red_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_intermediate_green' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green', 40, new Vector(1, 1, 1, 1)), 'tile_intermediate_green_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_advanced' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '', 40, new Vector(1, 1, 1, 1)), 'tile_advanced_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_advanced_blue' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue', 40, new Vector(1, 1, 1, 1)), 'tile_advanced_blue_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_advanced_green' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green', 40, new Vector(1, 1, 1, 1)), 'tile_advanced_green_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_underside' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_underside.png', '', 40, new Vector(1, 1, 1, 1)), // 4x4 Variant 'tile_beginner_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '', 40, new Vector(1, 1, 1, 1), 4), 'tile_beginner_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_beginner_red_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red', 40, new Vector(1, 1, 1, 1), 4), 'tile_beginner_red_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_beginner_blue_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue', 40, new Vector(1, 1, 1, 1), 4), 'tile_beginner_blue_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_intermediate_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '', 40, new Vector(1, 1, 1, 1), 4), 'tile_intermediate_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_intermediate_red_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, new Vector(1, 1, 1, 1), 4), 'tile_intermediate_red_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_intermediate_green_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green', 40, new Vector(1, 1, 1, 1), 4), 'tile_intermediate_green_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_advanced_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '', 40, new Vector(1, 1, 1, 1), 4), 'tile_advanced_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_advanced_blue_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue', 40, new Vector(1, 1, 1, 1), 4), 'tile_advanced_blue_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), 'tile_advanced_green_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green', 40, new Vector(1, 1, 1, 1), 4), 'tile_advanced_green_shadow_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2), 4), // Others 'wall_beginner' => (onFinish) -> createDefaultNormalMaterial(onFinish, 'data/textures/wall_beginner.png', 12, new Vector(0.8, 0.8, 0.6, 1)), 'edge_white' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/edge_white.png', 'data/textures/edge.normal.png', 50, new Vector(0.8, 0.8, 0.8, 1)), 'edge_white_shadow' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/edge_white_shadow.png', 'data/textures/edge.normal.png', 50, new Vector(0.2, 0.2, 0.2, 0.2)), 'beam' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/beam.png', 'data/textures/beam.normal.png', 12, new Vector(0.8, 0.8, 0.6, 1)), 'beam_side' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/beam_side.png', 'data/textures/beam_side.normal.png', 12, new Vector(0.8, 0.8, 0.6, 1)), 'friction_low' => (onFinish) -> createDefaultCubemapMaterial(onFinish, 'data/textures/friction_low.png', 'data/textures/friction_low.normal.png', 128, new Vector(1, 1, 1, 0.8)), 'friction_low_shadow' => (onFinish) -> createDefaultCubemapMaterial(onFinish, 'data/textures/friction_low_shadow.png', 'data/textures/friction_low.normal.png', 128, new Vector(0.3, 0.3, 0.35, 1)), 'friction_high' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/friction_high.png', 'data/textures/friction_high.normal.png', 10, new Vector(0.3, 0.3, 0.35, 1)), 'friction_high_shadow' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/friction_high_shadow.png', 'data/textures/friction_high.normal.png', 10, new Vector(0.15, 0.15, 0.16, 1.0)), 'stripe_caution' => (onFinish) -> createDefaultNormalMaterial(onFinish, 'data/textures/stripe_caution.png', 12, new Vector(0.8, 0.8, 0.6, 1)), ]; public static function loadDif(path:String, itr:InteriorObject, onFinish:Void->Void, ?so:Int = -1, makeCollideable = true) { #if (js || android) path = StringTools.replace(path, "data/", ""); #end function stripTexName(tex:String) { var dotpos = tex.lastIndexOf("."); var slashpos = tex.lastIndexOf("/") + 1; if (dotpos == -1) { dotpos = tex.length; } if (slashpos == -1) { slashpos = 0; } return tex.substring(slashpos, dotpos); } ResourceLoader.load(path).entry.load(() -> { var dif:Dif = null; var cache:DifCache = null; var cachePath = '${path}${so}'; if (difCache.exists(cachePath)) { cache = difCache.get(cachePath); dif = cache.dif; } else { var difresource = ResourceLoader.loadInterior(path); difresource.acquire(); dif = difresource.resource; dumbDownDif(dif); } var geo = (so == -1 ? dif.interiors[0] : dif.subObjects[so]); var triangles = []; var textures = []; var collider = new CollisionEntity(itr); var vertexBuckets = new Map>(); var edges = []; var colliderSurfaces = []; var mats = new Map>(); var difEdges:Map = []; if (cache != null) { mats = cache.difTriangles; } if (cache == null || makeCollideable) { mats = []; colliderSurfaces = []; difEdges = []; for (i in 0...geo.surfaces.length) { var surfaceindex = i; var surface = geo.surfaces[surfaceindex]; if (surface == null) continue; var planeindex = surface.planeIndex; var planeFlipped = (planeindex & 0x8000) == 0x8000; if (planeFlipped) planeindex &= ~0x8000; var plane = geo.planes[planeindex]; var normal = geo.normals[plane.normalIndex]; if (planeFlipped) normal = normal.scalar(-1); var texture = geo.materialList[surface.textureIndex]; if (!textures.contains(texture)) textures.push(texture); var points = geo.points; var colliderSurface = new CollisionSurface(); colliderSurface.points = []; colliderSurface.normals = []; colliderSurface.indices = []; colliderSurface.transformKeys = []; colliderSurface.originalIndices = []; colliderSurface.originalSurfaceIndex = surfaceindex; // Pick the three non-collinear points var texPlanes = geo.texGenEQs[surface.texGenIndex]; var startIdx = 0; while (true) { var pc0 = geo.points[geo.windings[surface.windingStart + startIdx + 1]]; var pc1 = geo.points[geo.windings[surface.windingStart + startIdx]]; var pc2 = geo.points[geo.windings[surface.windingStart + startIdx + 2]]; var tc0 = new Vector(texPlanes.planeX.x * pc0.x + texPlanes.planeX.y * pc0.y + texPlanes.planeX.z * pc0.z + texPlanes.planeX.d, texPlanes.planeY.x * pc0.x + texPlanes.planeY.y * pc0.y + texPlanes.planeY.z * pc0.z + texPlanes.planeY.d, 0, 0); var tc1 = new Vector(texPlanes.planeX.x * pc1.x + texPlanes.planeX.y * pc1.y + texPlanes.planeX.z * pc1.z + texPlanes.planeX.d, texPlanes.planeY.x * pc1.x + texPlanes.planeY.y * pc1.y + texPlanes.planeY.z * pc1.z + texPlanes.planeY.d, 0, 0); var tc2 = new Vector(texPlanes.planeX.x * pc2.x + texPlanes.planeX.y * pc2.y + texPlanes.planeX.z * pc2.z + texPlanes.planeX.d, texPlanes.planeY.x * pc2.x + texPlanes.planeY.y * pc2.y + texPlanes.planeY.z * pc2.z + texPlanes.planeY.d, 0, 0); var edge1 = new Vector(pc1.x - pc0.x, tc1.x - tc0.x, tc1.y - tc0.y); var edge2 = new Vector(pc2.x - pc0.x, tc2.x - tc0.x, tc2.y - tc0.y); var cp = edge1.cross(edge2); if (cp.lengthSq() > 1e-12) { break; } startIdx += 3; if (startIdx >= surface.windingCount) { startIdx = 0; break; } } var pc0 = geo.points[geo.windings[surface.windingStart + startIdx + 1]]; var pc1 = geo.points[geo.windings[surface.windingStart + startIdx]]; var pc2 = geo.points[geo.windings[surface.windingStart + startIdx + 2]]; var tc0 = new Vector(texPlanes.planeX.x * pc0.x + texPlanes.planeX.y * pc0.y + texPlanes.planeX.z * pc0.z + texPlanes.planeX.d, texPlanes.planeY.x * pc0.x + texPlanes.planeY.y * pc0.y + texPlanes.planeY.z * pc0.z + texPlanes.planeY.d, 0, 0); var tc1 = new Vector(texPlanes.planeX.x * pc1.x + texPlanes.planeX.y * pc1.y + texPlanes.planeX.z * pc1.z + texPlanes.planeX.d, texPlanes.planeY.x * pc1.x + texPlanes.planeY.y * pc1.y + texPlanes.planeY.z * pc1.z + texPlanes.planeY.d, 0, 0); var tc2 = new Vector(texPlanes.planeX.x * pc2.x + texPlanes.planeX.y * pc2.y + texPlanes.planeX.z * pc2.z + texPlanes.planeX.d, texPlanes.planeY.x * pc2.x + texPlanes.planeY.y * pc2.y + texPlanes.planeY.z * pc2.z + texPlanes.planeY.d, 0, 0); var edge1 = new Vector(pc1.x - pc0.x, tc1.x - tc0.x, tc1.y - tc0.y); var edge2 = new Vector(pc2.x - pc0.x, tc2.x - tc0.x, tc2.y - tc0.y); var cp = edge1.cross(edge2); var s = new Vector(); var t = new Vector(); if (Math.abs(cp.x) > 1e-12) { s.x = -cp.y / cp.x; t.x = -cp.z / cp.x; } edge1.set(pc1.y - pc0.y, tc1.x - tc0.x, tc1.y - tc0.y); edge2.set(pc2.y - pc0.y, tc2.x - tc0.x, tc2.y - tc0.y); cp.load(edge1.cross(edge2)); if (Math.abs(cp.x) > 1e-12) { s.y = -cp.y / cp.x; t.y = -cp.z / cp.x; } edge1.set(pc1.z - pc0.z, tc1.x - tc0.x, tc1.y - tc0.y); edge2.set(pc2.z - pc0.z, tc2.x - tc0.x, tc2.y - tc0.y); cp.load(edge1.cross(edge2)); if (Math.abs(cp.x) > 1e-12) { s.z = -cp.y / cp.x; t.z = -cp.z / cp.x; } s.normalize(); t.normalize(); var st = s.cross(t); if (st.x * normal.x + st.y * normal.y + st.z * normal.z < 0) { st.scale(-1); } // s.x *= -1; // t.x *= -1; // st.x *= -1; for (k in (surface.windingStart + 2)...(surface.windingStart + surface.windingCount)) { var p1, p2, p3; var i1, i2, i3; if ((k - (surface.windingStart + 2)) % 2 == 0) { i1 = k; i2 = k - 1; i3 = k - 2; p1 = points[geo.windings[k]]; p2 = points[geo.windings[k - 1]]; p3 = points[geo.windings[k - 2]]; if (makeCollideable) { colliderSurface.originalIndices.push(geo.windings[k]); colliderSurface.originalIndices.push(geo.windings[k - 1]); colliderSurface.originalIndices.push(geo.windings[k - 2]); } } else { i1 = k - 2; i2 = k - 1; i3 = k; p1 = points[geo.windings[k - 2]]; p2 = points[geo.windings[k - 1]]; p3 = points[geo.windings[k]]; if (makeCollideable) { colliderSurface.originalIndices.push(geo.windings[k - 2]); colliderSurface.originalIndices.push(geo.windings[k - 1]); colliderSurface.originalIndices.push(geo.windings[k]); } } var e1 = new TriangleEdge(geo.windings[k], geo.windings[k - 1], geo.windings[k - 2], surfaceindex); var e2 = new TriangleEdge(geo.windings[k - 1], geo.windings[k - 2], geo.windings[k], surfaceindex); var e3 = new TriangleEdge(geo.windings[k], geo.windings[k - 2], geo.windings[k - 1], surfaceindex); edges.push(e1); edges.push(e2); edges.push(e3); var texgen = geo.texGenEQs[surface.texGenIndex]; var uv1 = new Point2F(p1.x * texgen.planeX.x + p1.y * texgen.planeX.y + p1.z * texgen.planeX.z + texgen.planeX.d, p1.x * texgen.planeY.x + p1.y * texgen.planeY.y + p1.z * texgen.planeY.z + texgen.planeY.d); var uv2 = new Point2F(p2.x * texgen.planeX.x + p2.y * texgen.planeX.y + p2.z * texgen.planeX.z + texgen.planeX.d, p2.x * texgen.planeY.x + p2.y * texgen.planeY.y + p2.z * texgen.planeY.z + texgen.planeY.d); var uv3 = new Point2F(p3.x * texgen.planeX.x + p3.y * texgen.planeX.y + p3.z * texgen.planeX.z + texgen.planeX.d, p3.x * texgen.planeY.x + p3.y * texgen.planeY.y + p3.z * texgen.planeY.z + texgen.planeY.d); var tri = new DifBuilderTriangle(); tri.texture = texture; tri.normal1 = normal; tri.normal2 = normal; tri.normal3 = normal; tri.p1 = p1; tri.p2 = p2; tri.p3 = p3; tri.uv1 = uv1; tri.uv2 = uv2; tri.uv3 = uv3; // if (geo.normalIndices != null && geo.normalIndices.length > 0) { // tri.t1 = geo.normals2[geo.normalIndices[3 * i1 + 3 * surface.windingStart]]; // tri.t1.x *= -1; // tri.n1 = geo.normals2[geo.normalIndices[3 * i1 + 1 + 3 * surface.windingStart]]; // tri.n1.x *= -1; // tri.b1 = geo.normals2[geo.normalIndices[3 * i1 + 2 + 3 * surface.windingStart]]; // tri.b1.x *= -1; // tri.t2 = geo.normals2[geo.normalIndices[3 * i2 + 3 * surface.windingStart]]; // tri.t2.x *= -1; // tri.n2 = geo.normals2[geo.normalIndices[3 * i2 + 1 + 3 * surface.windingStart]]; // tri.n2.x *= -1; // tri.b2 = geo.normals2[geo.normalIndices[3 * i2 + 2 + 3 * surface.windingStart]]; // tri.b2.x *= -1; // tri.t3 = geo.normals2[geo.normalIndices[3 * i3 + 3 * surface.windingStart]]; // tri.t3.x *= -1; // tri.n3 = geo.normals2[geo.normalIndices[3 * i3 + 1 + 3 * surface.windingStart]]; // tri.n3.x *= -1; // tri.b3 = geo.normals2[geo.normalIndices[3 * i3 + 2 + 3 * surface.windingStart]]; // tri.b3.x *= -1; // } else { tri.t1 = new Point3F(s.x, s.y, s.z); tri.n1 = new Point3F(st.x, st.y, st.z); tri.b1 = new Point3F(t.x, t.y, t.z); tri.t2 = new Point3F(s.x, s.y, s.z); tri.n2 = new Point3F(st.x, st.y, st.z); tri.b2 = new Point3F(t.x, t.y, t.z); tri.t3 = new Point3F(s.x, s.y, s.z); tri.n3 = new Point3F(st.x, st.y, st.z); tri.b3 = new Point3F(t.x, t.y, t.z); // } triangles.push(tri); var materialName = stripTexName(texture).toLowerCase(); var hasMaterialInfo = materialDict.exists(materialName); if (hasMaterialInfo) { var minfo = materialDict.get(materialName); if (makeCollideable) { colliderSurface.friction = minfo.friction; colliderSurface.restitution = minfo.restitution; colliderSurface.force = minfo.force != null ? minfo.force : 0; } } if (makeCollideable) { colliderSurface.addPoint(-p1.x, p1.y, p1.z); colliderSurface.addPoint(-p2.x, p2.y, p2.z); colliderSurface.addPoint(-p3.x, p3.y, p3.z); colliderSurface.addNormal(-normal.x, normal.y, normal.z); colliderSurface.addNormal(-normal.x, normal.y, normal.z); colliderSurface.addNormal(-normal.x, normal.y, normal.z); colliderSurface.indices.push(colliderSurface.indices.length); colliderSurface.indices.push(colliderSurface.indices.length); colliderSurface.indices.push(colliderSurface.indices.length); colliderSurface.transformKeys.push(0); colliderSurface.transformKeys.push(0); colliderSurface.transformKeys.push(0); } for (v in [p1, p2, p3]) { var buckets = vertexBuckets.get(v); if (buckets == null) { buckets = []; vertexBuckets.set(v, buckets); } var bucket:VertexBucket = null; for (j in 0...buckets.length) { bucket = buckets[j]; if (normal.dot(bucket.referenceNormal) > Math.cos(Math.PI / 12)) break; bucket = null; } if (bucket == null) { bucket = { referenceNormal: normal, triangleIndices: [], normals: [], }; buckets.push(bucket); } bucket.triangleIndices.push(triangles.length - 1); bucket.normals.push(normal); } } if (makeCollideable) { colliderSurface.generateBoundingBox(); collider.addSurface(colliderSurface); colliderSurfaces.push(colliderSurface); } } var edgeMap:Map = new Map(); var internalEdges:Map = new Map(); for (edge in edges) { var edgeHash = edge.index1 >= edge.index2 ? edge.index1 * edge.index1 + edge.index1 + edge.index2 : edge.index1 + edge.index2 * edge.index2; if (internalEdges.exists(edgeHash)) continue; if (edgeMap.exists(edgeHash)) { if (edgeMap[edgeHash].surfaceIndex == edge.surfaceIndex) { // Internal edge internalEdges.set(edgeHash, true); edgeMap.remove(edgeHash); // trace('Removing internal edge: ${edge.index1} ${edge.index2}'); } else { var difEdge = new Edge(edge.index1, edge.index2, edge.surfaceIndex, edgeMap[edgeHash].surfaceIndex); difEdge.farPoint0 = edge.farPoint; difEdge.farPoint1 = edgeMap[edgeHash].farPoint; difEdges.set(edgeHash, difEdge); // Literal edge } } else { edgeMap.set(edgeHash, edge); } } function hashEdge(i1:Int, i2:Int) { return i1 >= i2 ? i1 * i1 + i1 + i2 : i1 + i2 * i2; } function getEdgeConcavity(edge:Edge) { var edgeSurface0 = edge.surfaceIndex0; var surface0 = geo.surfaces[edgeSurface0]; var planeindex = surface0.planeIndex; var planeFlipped = (planeindex & 0x8000) == 0x8000; if (planeFlipped) planeindex &= ~0x8000; var plane = geo.planes[planeindex]; var normal0 = geo.normals[plane.normalIndex]; if (planeFlipped) normal0 = normal0.scalar(-1); var edgeSurface1 = edge.surfaceIndex1; var surface1 = geo.surfaces[edgeSurface1]; planeindex = surface1.planeIndex; planeFlipped = (planeindex & 0x8000) == 0x8000; if (planeFlipped) planeindex &= ~0x8000; plane = geo.planes[planeindex]; var normal1 = geo.normals[plane.normalIndex]; if (planeFlipped) normal1 = normal1.scalar(-1); var dot = normal0.dot(normal1); if (Math.abs(dot) < 0.1) return false; var farP0 = geo.points[edge.farPoint0]; var farP1 = geo.points[edge.farPoint1]; var diff = farP1.sub(farP0); var cdot0 = normal0.dot(diff); if (cdot0 > -0.1) { return true; } return false; } function getEdgeNormal(edge:Edge) { var edgeSurface0 = edge.surfaceIndex0; var surface0 = geo.surfaces[edgeSurface0]; var planeindex = surface0.planeIndex; var planeFlipped = (planeindex & 0x8000) == 0x8000; if (planeFlipped) planeindex &= ~0x8000; var plane = geo.planes[planeindex]; var normal0 = geo.normals[plane.normalIndex]; if (planeFlipped) normal0 = normal0.scalar(-1); var edgeSurface1 = edge.surfaceIndex1; var surface1 = geo.surfaces[edgeSurface1]; planeindex = surface1.planeIndex; planeFlipped = (planeindex & 0x8000) == 0x8000; if (planeFlipped) planeindex &= ~0x8000; plane = geo.planes[planeindex]; var normal1 = geo.normals[plane.normalIndex]; if (planeFlipped) normal1 = normal1.scalar(-1); var norm = normal0.add(normal1).scalarDiv(2).normalized(); var vec = new Vector(norm.x, norm.y, norm.z); return vec; } var time = Console.time(); for (vtex => buckets in vertexBuckets) { for (i in 0...buckets.length) { var bucket = buckets[i]; var avgNormal = new Point3F(); for (normal in bucket.normals) avgNormal = avgNormal.add(normal); avgNormal = avgNormal.scalarDiv(bucket.normals.length); for (j in 0...bucket.triangleIndices.length) { var index = bucket.triangleIndices[j]; var tri = triangles[index]; if (tri.p1 == vtex) { tri.normal1 = avgNormal; tri.n1 = avgNormal; tri.t1 = tri.t1.sub(avgNormal.scalar(avgNormal.dot(tri.t1))).normalized(); tri.b1 = tri.b1.sub(avgNormal.scalar(avgNormal.dot(tri.b1))).normalized(); } if (tri.p2 == vtex) { tri.normal2 = avgNormal; tri.n2 = avgNormal; tri.t2 = tri.t2.sub(avgNormal.scalar(avgNormal.dot(tri.t2))).normalized(); tri.b2 = tri.b2.sub(avgNormal.scalar(avgNormal.dot(tri.b2))).normalized(); } if (tri.p3 == vtex) { tri.normal3 = avgNormal; tri.n3 = avgNormal; tri.t3 = tri.t3.sub(avgNormal.scalar(avgNormal.dot(tri.t3))).normalized(); tri.b3 = tri.b3.sub(avgNormal.scalar(avgNormal.dot(tri.b3))).normalized(); } } } } for (index => value in triangles) { if (mats.exists(value.texture)) { mats[value.texture].push(value); } else { mats.set(value.texture, [value]); } } var interval = Console.time() - time; Console.log('Normal smoothing build time: ${interval}'); } if (makeCollideable) { collider.finalize(); itr.collider = collider; } var buildNewCache = false; if (cache == null) { cache = new DifCache(); cache.difTriangles = mats; cache.dif = dif; cache.prim = null; buildNewCache = true; difCache.set(cachePath, cache); } function canFindTex(tex:String) { if (["NULL"].contains(tex)) { return false; } if (tex.indexOf('/') != -1) { tex = tex.split('/')[1]; } #if (js || android) path = StringTools.replace(path, "data/", ""); #end if (ResourceLoader.exists("data/textures/" + tex + ".jpg")) { return true; } if (ResourceLoader.exists("data/textures/" + tex + ".png")) { return true; } return false; } function tex(tex:String):String { if (tex.indexOf('/') != -1) { tex = tex.split('/')[1]; } if (ResourceLoader.exists("data/textures/" + tex + ".jpg")) { return "data/textures/" + tex + ".jpg"; } if (ResourceLoader.exists("data/textures/" + tex + ".png")) { return "data/textures/" + tex + ".png"; } return null; } var loadtexs = []; for (grp => tris in mats) { if (canFindTex(grp)) { loadtexs.push(tex(grp)); } } #if hl var memStats = hl.Gc.stats(); trace('Interior: ${path} ${memStats.currentMemory} ${memStats.totalAllocated} ${memStats.allocationCount}'); #end var worker = new ResourceLoaderWorker(() -> { var shaderWorker = new ResourceLoaderWorker(() -> { onFinish(); }); var time = Console.time(); var prim:Polygon = null; if (!buildNewCache && cache != null) { prim = cache.prim; } else { prim = new Polygon(); cache.prim = prim; } var materials = []; for (grp => tris in mats) { if (buildNewCache || cache == null) { var points = []; var normals = []; var uvs = []; var t = []; var b = []; var n = []; for (tri in tris) { var p1 = new Point(-tri.p1.x, tri.p1.y, tri.p1.z); var p2 = new Point(-tri.p2.x, tri.p2.y, tri.p2.z); var p3 = new Point(-tri.p3.x, tri.p3.y, tri.p3.z); var n1 = new Point(-tri.normal1.x, tri.normal1.y, tri.normal1.z); var n2 = new Point(-tri.normal2.x, tri.normal2.y, tri.normal2.z); var n3 = new Point(-tri.normal3.x, tri.normal3.y, tri.normal3.z); var uv1 = new UV(tri.uv1.x, tri.uv1.y); var uv2 = new UV(tri.uv2.x, tri.uv2.y); var uv3 = new UV(tri.uv3.x, tri.uv3.y); points.push(p3); points.push(p2); points.push(p1); normals.push(n3); normals.push(n2); normals.push(n1); uvs.push(uv3); uvs.push(uv2); uvs.push(uv1); t.push(new Point(-tri.t3.x, tri.t3.y, tri.t3.z)); t.push(new Point(-tri.t2.x, tri.t2.y, tri.t2.z)); t.push(new Point(-tri.t1.x, tri.t1.y, tri.t1.z)); b.push(new Point(-tri.b3.x, tri.b3.y, tri.b3.z)); b.push(new Point(-tri.b2.x, tri.b2.y, tri.b2.z)); b.push(new Point(-tri.b1.x, tri.b1.y, tri.b1.z)); n.push(new Point(-tri.n3.x, tri.n3.y, tri.n3.z)); n.push(new Point(-tri.n2.x, tri.n2.y, tri.n2.z)); n.push(new Point(-tri.n1.x, tri.n1.y, tri.n1.z)); } prim.appendPoints(points); prim.appendUVs(uvs); prim.appendNormals(normals); prim.appendTangents(t); prim.appendBitangents(b); prim.appendTexMatNormals(n); prim.nextMaterial(); } var texture:Texture; var material:Material; var exactName = stripTexName(StringTools.replace(grp.toLowerCase(), "textures/", "")); if (canFindTex(grp) || shaderMaterialDict.exists(exactName)) { material = h3d.mat.Material.create(); if (shaderMaterialDict.exists(exactName)) { var retrievefunc = shaderMaterialDict[exactName]; shaderWorker.addTask(fwd -> { retrievefunc(shad -> { var zPass = material.mainPass.clone(); zPass.removeShader(material.textureShader); var tx = zPass.getShader(h3d.shader.Texture); zPass.removeShader(tx); zPass.setColorMask(false, false, false, false); zPass.depthWrite = true; zPass.setPassName("zPass"); material.addPass(zPass); material.mainPass.depthTest = LessEqual; material.mainPass.removeShader(material.textureShader); material.mainPass.addShader(shad); var thisprops:Dynamic = material.getDefaultProps(); thisprops.light = false; // We will calculate our own lighting material.props = thisprops; material.shadows = false; material.receiveShadows = false; material.mainPass.setPassName("interior"); fwd(); }); }); } else { texture = ResourceLoader.getTextureRealpath(tex(grp)).resource; // ResourceLoader.getTexture(tex(grp), false).resource; texture.wrap = Wrap.Repeat; texture.mipMap = Linear; material.texture = texture; material.shadows = false; material.receiveShadows = false; } } else { Console.warn('Unable to load ${grp} texture for dif ${path}'); material = Material.create(); material.shadows = false; material.receiveShadows = false; } // material.mainPass.addShader(new h3d.shader.pbr.PropsValues(1, 0, 0, 1)); if (Debug.wireFrame) material.mainPass.wireframe = true; materials.push(material); } if (buildNewCache || cache == null) { prim.endPrimitive(); } var mesh = new MultiMaterial(prim, materials, itr); var interval = Console.time() - time; Console.log('Geometry build time ${interval}'); time = Console.time(); shaderWorker.run(); interval = Console.time() - time; Console.log('Shader building time ${interval}'); }); for (f in loadtexs) { worker.loadFile(f); } worker.run(); }); } // Keeps only relevant parts of the dif to reduce memory footprint static function dumbDownDif(dif:Dif) { dif.aiSpecialNodes = null; dif.forceFields = null; dif.triggers = null; dif.gameEntities = null; dif.interiorPathfollowers = null; dif.triggers = null; dif.vehicleCollision = null; for (itr in dif.interiors.concat(dif.subObjects)) { itr.alarmAmbientColor = null; itr.alarmLMapIndices = null; itr.animatedLights = null; itr.baseAmbientColor = null; itr.bspNodes = null; itr.bspSolidLeaves = null; itr.convexHullEmitStrings = null; itr.convexHulls = null; itr.coordBinIndices = null; itr.boundingSphere = null; itr.coordBins = null; itr.edges = null; itr.edges2 = null; itr.hullEmitStringIndices = null; itr.hullIndices = null; itr.hullPlaneIndices = null; itr.hullSurfaceIndices = null; itr.lightMaps = null; itr.lightStates = null; itr.nameBuffer = null; itr.normalIndices = null; itr.normalLMapIndices = null; itr.nullSurfaces = null; itr.pointVisibilities = null; itr.polyListPlanes = null; itr.polyListPoints = null; itr.polyListStrings = null; itr.portals = null; itr.solidLeafSurfaces = null; itr.stateDataBuffers = null; itr.zones = null; itr.zoneSurfaces = null; itr.zoneStaticMeshes = null; itr.windingIndices = null; itr.texNormals = null; itr.texMatrices = null; itr.texMatIndices = null; itr.stateDatas = null; } } }