@woosh/meep-engine 2.164.0 → 2.165.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BitImage2.d.ts +91 -0
- package/src/core/binary/BitImage2.d.ts.map +1 -0
- package/src/core/binary/BitImage2.js +242 -0
- package/src/core/geom/2d/oriented-box/min_area_rect_2d.d.ts +22 -0
- package/src/core/geom/2d/oriented-box/min_area_rect_2d.d.ts.map +1 -0
- package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -0
- package/src/core/geom/2d/packing/skyline.d.ts +116 -0
- package/src/core/geom/2d/packing/skyline.d.ts.map +1 -0
- package/src/core/geom/2d/packing/skyline.js +258 -0
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts +18 -0
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts.map +1 -0
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +86 -0
- package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -0
- package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -0
- package/src/core/geom/3d/atlas/Chart.d.ts +58 -0
- package/src/core/geom/3d/atlas/Chart.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/Chart.js +77 -0
- package/src/core/geom/3d/atlas/REVIEW_02.md +392 -0
- package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts +73 -0
- package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -0
- package/src/core/geom/3d/atlas/atlas_chart_type.d.ts +17 -0
- package/src/core/geom/3d/atlas/atlas_chart_type.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -0
- package/src/core/geom/3d/atlas/atlas_compute_charts.d.ts +25 -0
- package/src/core/geom/3d/atlas/atlas_compute_charts.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -0
- package/src/core/geom/3d/atlas/atlas_extract_chart.d.ts +17 -0
- package/src/core/geom/3d/atlas/atlas_extract_chart.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -0
- package/src/core/geom/3d/atlas/atlas_generate.d.ts +39 -0
- package/src/core/geom/3d/atlas/atlas_generate.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_generate.js +43 -0
- package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts +84 -0
- package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -0
- package/src/core/geom/3d/atlas/atlas_write_chart_uvs.d.ts +25 -0
- package/src/core/geom/3d/atlas/atlas_write_chart_uvs.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -0
- package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.d.ts +22 -0
- package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -0
- package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.d.ts +9 -0
- package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -0
- package/src/core/geom/3d/atlas/data/atlas_build_face_areas.d.ts +9 -0
- package/src/core/geom/3d/atlas/data/atlas_build_face_areas.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -0
- package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.d.ts +27 -0
- package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -0
- package/src/core/geom/3d/atlas/io/bt_loop_uv.d.ts +22 -0
- package/src/core/geom/3d/atlas/io/bt_loop_uv.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/io/bt_loop_uv.js +57 -0
- package/src/core/geom/3d/atlas/pack/ChartPlacement.d.ts +36 -0
- package/src/core/geom/3d/atlas/pack/ChartPlacement.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -0
- package/src/core/geom/3d/atlas/pack/atlas_pack_charts.d.ts +62 -0
- package/src/core/geom/3d/atlas/pack/atlas_pack_charts.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/pack/atlas_pack_charts.js +551 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_param_validity.d.ts +22 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_param_validity.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_param_validity.js +73 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts +37 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +270 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts +23 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +224 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.d.ts +14 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.d.ts +22 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -0
- package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.d.ts +20 -0
- package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -0
- package/src/core/geom/3d/atlas/param/fit_plane_basis.d.ts +27 -0
- package/src/core/geom/3d/atlas/param/fit_plane_basis.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -0
- package/src/core/geom/3d/atlas/segment/RegionGraph.d.ts +31 -0
- package/src/core/geom/3d/atlas/segment/RegionGraph.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -0
- package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.d.ts +23 -0
- package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -0
- package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.d.ts +24 -0
- package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -0
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts +27 -0
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +113 -0
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts.d.ts +44 -0
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +275 -0
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.d.ts +22 -0
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.d.ts.map +1 -0
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +199 -0
- package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.d.ts +32 -0
- package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.d.ts.map +1 -0
- package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -0
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts +20 -0
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts.map +1 -0
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +66 -0
- package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.d.ts.map +1 -1
- package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +13 -11
- package/src/core/geom/3d/sphere/sphere_compute_bounding_of_2_spheres.d.ts.map +1 -1
- package/src/core/geom/3d/sphere/sphere_compute_bounding_of_2_spheres.js +4 -3
- package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +4 -3
- package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.js +4 -3
- package/src/core/geom/3d/topology/simplify/quadratic/apply_border_penalty_to_vertex_quadrics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/quadratic/apply_border_penalty_to_vertex_quadrics.js +10 -6
- package/src/core/geom/3d/topology/struct/TopoMesh.js +2 -2
- package/src/core/geom/3d/topology/struct/TopoTriangle.d.ts +0 -2
- package/src/core/geom/3d/topology/struct/TopoTriangle.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/TopoTriangle.js +2 -2
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +8 -27
- package/src/core/geom/vec3/v3_cotangent.d.ts +20 -0
- package/src/core/geom/vec3/v3_cotangent.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_cotangent.js +29 -0
- package/src/core/geom/vec3/v3_matrix3_multiply.d.ts +19 -0
- package/src/core/geom/vec3/v3_matrix3_multiply.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -0
- package/src/core/geom/vec3/v3_multiply.d.ts +16 -0
- package/src/core/geom/vec3/v3_multiply.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_multiply.js +19 -0
- package/src/core/math/linalg/cg/cg_solve.d.ts +24 -0
- package/src/core/math/linalg/cg/cg_solve.d.ts.map +1 -0
- package/src/core/math/linalg/cg/cg_solve.js +106 -0
- package/src/core/math/linalg/cg/cg_solve_normal_equations.d.ts +21 -0
- package/src/core/math/linalg/cg/cg_solve_normal_equations.d.ts.map +1 -0
- package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts +19 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts.map +1 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -0
- package/src/core/math/linalg/sparse/SparseMatrixCSR.d.ts +50 -0
- package/src/core/math/linalg/sparse/SparseMatrixCSR.d.ts.map +1 -0
- package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -0
- package/src/core/math/linalg/sparse/spm_matvec.d.ts +9 -0
- package/src/core/math/linalg/sparse/spm_matvec.d.ts.map +1 -0
- package/src/core/math/linalg/sparse/spm_matvec.js +22 -0
- package/src/core/math/linalg/sparse/spm_matvec_transpose.d.ts +12 -0
- package/src/core/math/linalg/sparse/spm_matvec_transpose.d.ts.map +1 -0
- package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -0
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.d.ts +39 -0
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.js +22 -1
- package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.d.ts.map +1 -1
- package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.js +32 -9
- package/src/core/math/physics/mie/compute_mie_phase.d.ts +2 -3
- package/src/core/math/physics/mie/compute_mie_phase.d.ts.map +1 -1
- package/src/core/math/physics/mie/compute_mie_phase.js +1 -3
- package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts +0 -1
- package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts.map +1 -1
- package/src/core/math/physics/mie/lorenz_mie_coefs.js +0 -7
- package/src/engine/ecs/attachment/TransformAttachmentBinding.js +3 -3
- package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +2 -2
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/Terrain.js +4 -3
- package/src/engine/ecs/terrain/tiles/TerrainTile.js +2 -2
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +9 -8
- package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics/GraphicsEngine.js +2 -1
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +2 -2
- package/src/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +2 -2
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +2 -2
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +0 -1
- package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +8 -7
- package/src/engine/graphics/geometry/MikkT/AvgTSpace.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/AvgTSpace.js +11 -6
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +2 -3
- package/src/engine/graphics/geometry/MikkT/CalcTexArea.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/CalcTexArea.js +3 -4
- package/src/engine/graphics/geometry/MikkT/DegenEpilogue.js +4 -4
- package/src/engine/graphics/geometry/MikkT/EvalTspace.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/EvalTspace.js +11 -8
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +7 -8
- package/src/engine/graphics/geometry/MikkT/GenerateTSpaces.js +8 -8
- package/src/engine/graphics/geometry/MikkT/InitTriInfo.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/InitTriInfo.js +30 -25
- package/src/engine/graphics/geometry/MikkT/MikkTSpace.js +7 -7
- package/src/engine/graphics/geometry/MikkT/NormalizeSafe.d.ts +0 -1
- package/src/engine/graphics/geometry/MikkT/NormalizeSafe.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/NormalizeSafe.js +6 -4
- package/src/engine/graphics/geometry/MikkT/STSpace.d.ts +0 -2
- package/src/engine/graphics/geometry/MikkT/STSpace.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/STSpace.js +3 -3
- package/src/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.d.ts +0 -2
- package/src/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.js +0 -1
- package/src/engine/graphics/impostors/octahedral/ImpostorBaker.d.ts +0 -2
- package/src/engine/graphics/impostors/octahedral/ImpostorBaker.d.ts.map +1 -1
- package/src/engine/graphics/impostors/octahedral/ImpostorBaker.js +4 -3
- package/src/engine/graphics/impostors/octahedral/grid/HemiOctahedralUvEncoder.js +2 -2
- package/src/engine/graphics/impostors/octahedral/grid/UvEncoder.d.ts +17 -7
- package/src/engine/graphics/impostors/voxel/VoxelImpostorBaker.js +2 -2
- package/src/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +3 -3
- package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +4 -3
- package/src/engine/graphics/render/forward_plus/LightManager.js +2 -2
- package/src/engine/graphics/render/forward_plus/model/Decal.d.ts +0 -2
- package/src/engine/graphics/render/forward_plus/model/Decal.d.ts.map +1 -1
- package/src/engine/graphics/render/forward_plus/model/Decal.js +3 -3
- package/src/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +4 -3
- package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +6 -4
- package/src/engine/graphics/sh3/lightmap/ARTIFACTS_PLAN.md +146 -0
- package/src/engine/graphics/sh3/lightmap/CODE_REVIEW.md +121 -0
- package/src/engine/graphics/sh3/lightmap/LIGHTMAPPER_REVIEW.md +183 -0
- package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.d.ts +90 -0
- package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.js +678 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_build_geometry.d.ts +32 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_build_geometry.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_build_geometry.js +155 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_denoise.d.ts +49 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_denoise.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_denoise.js +136 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_dilate.d.ts +19 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_dilate.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_dilate.js +89 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_gather.d.ts +54 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_gather.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_gather.js +168 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.d.ts +30 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.js +145 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_seam_fix.d.ts +38 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_seam_fix.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/lightmap_seam_fix.js +152 -0
- package/src/engine/graphics/sh3/lightmap/prototypeLightmapBaker.d.ts +2 -0
- package/src/engine/graphics/sh3/lightmap/prototypeLightmapBaker.d.ts.map +1 -0
- package/src/engine/graphics/sh3/lightmap/prototypeLightmapBaker.js +186 -0
- package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.d.ts.map +1 -1
- package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +2 -2
- package/src/engine/graphics/sh3/path_tracer/PathTracer.js +2 -2
- package/src/engine/graphics/sh3/path_tracer/prepare_scene_gltf.d.ts.map +1 -1
- package/src/engine/graphics/sh3/path_tracer/prepare_scene_gltf.js +6 -4
- package/src/engine/graphics/sh3/path_tracer/prototypePathTracer.js +3 -3
- package/src/engine/graphics/sh3/path_tracer/texture/sample_material.d.ts.map +1 -1
- package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +5 -4
- package/src/engine/graphics/sh3/sky/hosek/prototype_hosek.js +2 -2
- package/src/engine/input/devices/KeyboardDevice.d.ts +61 -14
- package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
- package/src/engine/input/devices/KeyboardDevice.js +41 -15
- package/src/engine/input/devices/isHTMLElementFocusable.d.ts.map +1 -1
- package/src/engine/input/devices/isHTMLElementFocusable.js +2 -9
- package/src/engine/sound/simulation/prototypeAcousticSolver.js +261 -143
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { LinearMipmapLinearFilter } from "three";
|
|
2
|
+
import { assert } from "../../../../core/assert.js";
|
|
3
|
+
import { rgb_to_luminance } from "../../../../core/color/rgb_to_luminance.js";
|
|
4
|
+
import { atlas_compute_charts } from "../../../../core/geom/3d/atlas/atlas_compute_charts.js";
|
|
5
|
+
import { atlas_pack_charts } from "../../../../core/geom/3d/atlas/pack/atlas_pack_charts.js";
|
|
6
|
+
import { m4_normal_matrix3 } from "../../../../core/geom/3d/mat4/m4_normal_matrix3.js";
|
|
7
|
+
import { BinaryTopology } from "../../../../core/geom/3d/topology/struct/binary/BinaryTopology.js";
|
|
8
|
+
import {
|
|
9
|
+
bt_mesh_from_indexed_geometry
|
|
10
|
+
} from "../../../../core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js";
|
|
11
|
+
import { v3_length } from "../../../../core/geom/vec3/v3_length.js";
|
|
12
|
+
import { v3_matrix3_rotate } from "../../../../core/geom/vec3/v3_matrix3_rotate.js";
|
|
13
|
+
import { v3_matrix4_multiply } from "../../../../core/geom/vec3/v3_matrix4_multiply.js";
|
|
14
|
+
import { v3_normalize_array } from "../../../../core/geom/vec3/v3_normalize_array.js";
|
|
15
|
+
import { seededRandom } from "../../../../core/math/random/seededRandom.js";
|
|
16
|
+
import Task from "../../../../core/process/task/Task.js";
|
|
17
|
+
import TaskGroup from "../../../../core/process/task/TaskGroup.js";
|
|
18
|
+
import { TaskSignal } from "../../../../core/process/task/TaskSignal.js";
|
|
19
|
+
import { Transform } from "../../../ecs/transform/Transform.js";
|
|
20
|
+
import { ShadedGeometry } from "../../ecs/mesh-v2/ShadedGeometry.js";
|
|
21
|
+
import { Sampler2D } from "../../texture/sampler/Sampler2D.js";
|
|
22
|
+
import { sampler2d_to_float32_texture } from "../../texture/sampler/sampler2d_to_float32_texture.js";
|
|
23
|
+
import { make_sky_rtiw } from "../path_tracer/make_sky_rtiw.js";
|
|
24
|
+
import { PathTracedScene } from "../path_tracer/PathTracedScene.js";
|
|
25
|
+
import { PathTracer } from "../path_tracer/PathTracer.js";
|
|
26
|
+
import { populate_path_traced_scene_from_ecd } from "../path_tracer/populate_path_traced_scene_from_ecd.js";
|
|
27
|
+
import { lightmap_build_geometry } from "./lightmap_build_geometry.js";
|
|
28
|
+
import { lightmap_denoise } from "./lightmap_denoise.js";
|
|
29
|
+
import { lightmap_dilate } from "./lightmap_dilate.js";
|
|
30
|
+
import { lightmap_gather } from "./lightmap_gather.js";
|
|
31
|
+
import { lightmap_rasterize_triangle } from "./lightmap_rasterize_triangle.js";
|
|
32
|
+
import { lightmap_seam_fix } from "./lightmap_seam_fix.js";
|
|
33
|
+
|
|
34
|
+
const PHASE_COARSE = 0;
|
|
35
|
+
const PHASE_FINE = 1;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Geometric-mean scale of a column-major affine 4x4, used to weight a chart's
|
|
39
|
+
* texel density by its WORLD surface area so density is roughly uniform across
|
|
40
|
+
* the scene regardless of per-entity scale.
|
|
41
|
+
*
|
|
42
|
+
* @param {Float32Array|number[]} m
|
|
43
|
+
* @returns {number}
|
|
44
|
+
*/
|
|
45
|
+
function mat4_mean_scale(m) {
|
|
46
|
+
const sx = v3_length(m[0], m[1], m[2]);
|
|
47
|
+
const sy = v3_length(m[4], m[5], m[6]);
|
|
48
|
+
const sz = v3_length(m[8], m[9], m[10]);
|
|
49
|
+
return Math.cbrt(sx * sy * sz);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read a geometry's index buffer, generating a trivial sequential one (without
|
|
54
|
+
* mutating the geometry) when it is non-indexed.
|
|
55
|
+
*
|
|
56
|
+
* @param {THREE.BufferGeometry} geometry
|
|
57
|
+
* @param {number} vertex_count
|
|
58
|
+
* @returns {number[]|Uint8Array|Uint16Array|Uint32Array}
|
|
59
|
+
*/
|
|
60
|
+
function geometry_indices(geometry, vertex_count) {
|
|
61
|
+
const index = geometry.getIndex();
|
|
62
|
+
if (index !== null && index !== undefined) {
|
|
63
|
+
return index.array;
|
|
64
|
+
}
|
|
65
|
+
const indices = new Uint32Array(vertex_count);
|
|
66
|
+
for (let i = 0; i < vertex_count; i++) {
|
|
67
|
+
indices[i] = i;
|
|
68
|
+
}
|
|
69
|
+
return indices;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Bake a single shared **indirect** lightmap for every {@link ShadedGeometry} in a
|
|
74
|
+
* scene (mixed-mode lighting: direct light stays realtime) using the engine's own
|
|
75
|
+
* UV-atlas charter ({@link atlas_compute_charts} + {@link atlas_pack_charts}) and
|
|
76
|
+
* the {@link PathTracer}.
|
|
77
|
+
*
|
|
78
|
+
* The returned {@link TaskGroup} runs the bake incrementally on an executor: its
|
|
79
|
+
* initializer conditions every mesh (build topology → compute UV charts), packs
|
|
80
|
+
* all charts from all meshes into ONE atlas, swaps each mesh's geometry for a
|
|
81
|
+
* re-charted one carrying a `uv2` lightmap channel, points every material at the
|
|
82
|
+
* shared lightmap texture (initially black), and rasterizes the covered texels
|
|
83
|
+
* conservatively into a bake job list. Subsequent cycles bake the texels and
|
|
84
|
+
* upload the partial result each cycle for a live preview.
|
|
85
|
+
*
|
|
86
|
+
* Quality/perf techniques (see LIGHTMAPPER_REVIEW.md):
|
|
87
|
+
* - **Adaptive sampling** — a coarse grid is fully path-traced first; interior
|
|
88
|
+
* texels whose coarse neighbourhood agrees are bilinearly interpolated, and
|
|
89
|
+
* only edges/discontinuities are fully sampled (`adaptive_step` = 1 disables).
|
|
90
|
+
* - **Conservative rasterization** — edge-overlapping texels get a sample clamped
|
|
91
|
+
* onto the triangle, so thin features and chart borders are covered.
|
|
92
|
+
* - **World-space off-surface bias** — a small fixed world-space nudge (scaled only
|
|
93
|
+
* by coordinate magnitude for float precision), NOT coupled to lightmap resolution.
|
|
94
|
+
* - **Confidence mask** — buried/over-occluded texels are left as holes and filled
|
|
95
|
+
* from good neighbours instead of baking in leaks.
|
|
96
|
+
* - **Variance-guided denoise** — noisy texels blend with consistent neighbours;
|
|
97
|
+
* real gradients are preserved.
|
|
98
|
+
*
|
|
99
|
+
* The lightmap stores cosine-weighted mean radiance (`E / π`, linear); three.js's
|
|
100
|
+
* lightmap path multiplies it back by `π · albedo / π = albedo`, so each surface
|
|
101
|
+
* keeps its colour modulated by the baked indirect light.
|
|
102
|
+
*
|
|
103
|
+
* @param {object} options
|
|
104
|
+
* @param {EntityComponentDataset} options.ecd the scene to bake
|
|
105
|
+
* @param {number} [options.lightmap_resolution] square atlas size in texels (default 2048)
|
|
106
|
+
* @param {number} [options.samples_per_texel] minimum hemisphere gather samples per fully-baked texel (default 32)
|
|
107
|
+
* @param {number} [options.max_samples] adaptive ceiling: noisy texels (contacts/edges) sample up to this (default 128)
|
|
108
|
+
* @param {number} [options.sample_variance_target] relative variance-of-mean to stop adaptive sampling; 0 = fixed count (default 0.02)
|
|
109
|
+
* @param {number} [options.min_bounce] path-tracer roulette start depth (default 2)
|
|
110
|
+
* @param {number} [options.max_bounce] path-tracer maximum depth (default 6)
|
|
111
|
+
* @param {number} [options.padding] atlas gutter texels, also the dilation radius (default 4)
|
|
112
|
+
* @param {number} [options.ray_batch] approximate number of gather/probe rays to cast per
|
|
113
|
+
* executor cycle (default 40000). The per-cycle texel count is derived from this and the
|
|
114
|
+
* per-texel ray cost (samples, bounce depth, confidence probes), so a cycle stays roughly
|
|
115
|
+
* constant work regardless of those — raising `samples_per_texel` shrinks the texel batch
|
|
116
|
+
* instead of lengthening each cycle, keeping the bake from hitching the frame rate.
|
|
117
|
+
* @param {number} [options.light_map_intensity] material lightMapIntensity to set (default 1)
|
|
118
|
+
* @param {number} [options.surface_bias] off-surface nudge as a fraction of the point's world-coordinate
|
|
119
|
+
* magnitude (a small world-space offset, decoupled from lightmap resolution); default 0.001
|
|
120
|
+
* @param {number} [options.adaptive_step] coarse grid spacing in texels; 1 = full sampling everywhere (default 4)
|
|
121
|
+
* @param {number} [options.interpolation_threshold] relative luminance agreement that marks an interior texel "smooth" (default 0.1)
|
|
122
|
+
* @param {number} [options.min_sample_fraction] fraction of `samples_per_texel` spent on smooth texels; 0 = pure bilinear (default 0.25)
|
|
123
|
+
* @param {number} [options.supersample] sub-texel ray-origin jitter as a fraction of a texel (anti-aliasing); 0 disables (default 1)
|
|
124
|
+
* @param {number} [options.confidence_probes] short backface probes per baked texel; 0 disables (default 6)
|
|
125
|
+
* @param {number} [options.inside_probe_texels] backface probe reach in texels (default 4)
|
|
126
|
+
* @param {number} [options.valid_threshold] minimum confidence to keep a texel; below it becomes a hole (default 0.15)
|
|
127
|
+
* @param {boolean} [options.denoise] run the variance-guided denoise pass (default true)
|
|
128
|
+
* @param {number} [options.seam_fix_iterations] UV-seam blend passes; 0 disables (default 4)
|
|
129
|
+
* @param {boolean} [options.debug] also expose per-texel world positions and the
|
|
130
|
+
* pre-dilation baked coverage on `group.lightmap` (extra buffers; default false)
|
|
131
|
+
* @param {function(out:number[], out_offset:number, direction:number[], direction_offset:number):void} [options.background]
|
|
132
|
+
* environment radiance sampler for sky/ambient fill (default {@link make_sky_rtiw})
|
|
133
|
+
* @param {number} [options.seed] PRNG seed for reproducible bakes (default 1)
|
|
134
|
+
* @returns {TaskGroup} a group whose single task performs the bake; it also exposes
|
|
135
|
+
* `.lightmap = { texture, sampler, scene }` for inspection
|
|
136
|
+
*/
|
|
137
|
+
export function bake_lightmap_for_scene({
|
|
138
|
+
ecd,
|
|
139
|
+
lightmap_resolution = 2048,
|
|
140
|
+
samples_per_texel = 32,
|
|
141
|
+
max_samples = 128,
|
|
142
|
+
sample_variance_target = 0.02,
|
|
143
|
+
min_bounce = 2,
|
|
144
|
+
max_bounce = 6,
|
|
145
|
+
padding = 4,
|
|
146
|
+
ray_batch = 40000,
|
|
147
|
+
light_map_intensity = 1,
|
|
148
|
+
surface_bias = 0.001,
|
|
149
|
+
adaptive_step = 4,
|
|
150
|
+
interpolation_threshold = 0.1,
|
|
151
|
+
min_sample_fraction = 0.25,
|
|
152
|
+
supersample = 1,
|
|
153
|
+
confidence_probes = 6,
|
|
154
|
+
inside_probe_texels = 4,
|
|
155
|
+
valid_threshold = 0.15,
|
|
156
|
+
denoise = true,
|
|
157
|
+
seam_fix_iterations = 4,
|
|
158
|
+
debug = false,
|
|
159
|
+
background = make_sky_rtiw(),
|
|
160
|
+
seed = 1,
|
|
161
|
+
}) {
|
|
162
|
+
assert.defined(ecd, 'ecd');
|
|
163
|
+
|
|
164
|
+
const resolution = lightmap_resolution;
|
|
165
|
+
const random = seededRandom(seed);
|
|
166
|
+
|
|
167
|
+
// Derive how many texels to process per executor cycle so a cycle's ray count
|
|
168
|
+
// stays bounded by ~ray_batch regardless of the cost knobs. Budget against the
|
|
169
|
+
// WORST case a full texel can incur — variance-adaptive sampling can grow a
|
|
170
|
+
// noisy texel up to max_samples — so a batch that lands on a cluster of noisy
|
|
171
|
+
// texels still can't blow past the budget and hitch the frame. Fine (reduced/
|
|
172
|
+
// interpolated) texels are cheaper, so this is a safe upper bound.
|
|
173
|
+
const avg_bounces = Math.max(1, 0.5 * (min_bounce + max_bounce));
|
|
174
|
+
const worst_samples = Math.max(samples_per_texel, max_samples);
|
|
175
|
+
const rays_per_texel = worst_samples * avg_bounces + confidence_probes;
|
|
176
|
+
const texel_batch = Math.max(1, Math.floor(ray_batch / rays_per_texel));
|
|
177
|
+
|
|
178
|
+
const atlas = Sampler2D.float32(4, resolution, resolution);
|
|
179
|
+
const coverage = new Uint8Array(resolution * resolution);
|
|
180
|
+
const variance = new Float32Array(resolution * resolution);
|
|
181
|
+
const texel_normals = new Float32Array(resolution * resolution * 3); // world normal per committed texel, for normal-aware denoise
|
|
182
|
+
const texel_world_pos = debug ? new Float32Array(resolution * resolution * 3) : null; // world position per committed texel (debug only)
|
|
183
|
+
const texture = sampler2d_to_float32_texture(atlas);
|
|
184
|
+
texture.generateMipmaps = true;
|
|
185
|
+
texture.minFilter = LinearMipmapLinearFilter;
|
|
186
|
+
texture.magFilter = LinearMipmapLinearFilter;
|
|
187
|
+
|
|
188
|
+
const scene = new PathTracedScene();
|
|
189
|
+
const path_tracer = new PathTracer();
|
|
190
|
+
|
|
191
|
+
// full texels sample adaptively: at least `samples_per_texel`, up to
|
|
192
|
+
// `max_samples`, drawing more where the relative noise is high (contacts,
|
|
193
|
+
// occlusion edges, high-frequency bounce).
|
|
194
|
+
const gather_cfg_full = {
|
|
195
|
+
samples: samples_per_texel,
|
|
196
|
+
max_samples: Math.max(samples_per_texel, max_samples),
|
|
197
|
+
variance_target: sample_variance_target,
|
|
198
|
+
min_bounce,
|
|
199
|
+
max_bounce,
|
|
200
|
+
confidence_probes,
|
|
201
|
+
inside_probe_texels,
|
|
202
|
+
supersample,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// smooth (interior) texels: a reduced real estimate rather than a pure lerp —
|
|
206
|
+
// catches small features a lerp would erase, denoise cleans the extra noise.
|
|
207
|
+
// 0 -> pure bilinear interpolation. Fixed count (no adaptive growth).
|
|
208
|
+
const reduced_samples = min_sample_fraction > 0
|
|
209
|
+
? Math.max(4, Math.round(samples_per_texel * min_sample_fraction))
|
|
210
|
+
: 0;
|
|
211
|
+
const gather_cfg_reduced = {
|
|
212
|
+
...gather_cfg_full,
|
|
213
|
+
samples: reduced_samples,
|
|
214
|
+
max_samples: reduced_samples,
|
|
215
|
+
variance_target: 0
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// bake jobs, filled by the initializer: one entry per covered atlas texel
|
|
219
|
+
let job_x = new Int32Array(0);
|
|
220
|
+
let job_y = new Int32Array(0);
|
|
221
|
+
let job_position = new Float32Array(0); // 3 per job, off-surface world point
|
|
222
|
+
let job_normal = new Float32Array(0); // 3 per job, world normal
|
|
223
|
+
let job_texel_size = new Float32Array(0); // 1 per job, world size of one texel
|
|
224
|
+
let coarse_indices = new Int32Array(0);
|
|
225
|
+
let fine_indices = new Int32Array(0);
|
|
226
|
+
let chart_groups = []; // one packed-chart list per mesh, for seam fixing
|
|
227
|
+
let baked_coverage = null; // snapshot of coverage before dilation (debug/tests)
|
|
228
|
+
|
|
229
|
+
let phase = PHASE_COARSE;
|
|
230
|
+
let coarse_cursor = 0;
|
|
231
|
+
let fine_cursor = 0;
|
|
232
|
+
|
|
233
|
+
const gather_out = new Float32Array(4);
|
|
234
|
+
|
|
235
|
+
function is_coarse(x, y) {
|
|
236
|
+
return adaptive_step <= 1 || (x % adaptive_step === 0 && y % adaptive_step === 0);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function prepare() {
|
|
240
|
+
// ensure transform matrices are current and consistent between the
|
|
241
|
+
// ray-traced scene and our own world-space sampling
|
|
242
|
+
ecd.traverseEntities([ShadedGeometry, Transform], (sg, transform) => {
|
|
243
|
+
transform.updateMatrix();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
scene.__background_sampler = background;
|
|
247
|
+
populate_path_traced_scene_from_ecd(ecd, scene);
|
|
248
|
+
scene.optimize();
|
|
249
|
+
|
|
250
|
+
const entries = [];
|
|
251
|
+
const all_charts = [];
|
|
252
|
+
|
|
253
|
+
ecd.traverseEntities([ShadedGeometry, Transform], (sg, transform) => {
|
|
254
|
+
const geometry = sg.geometry;
|
|
255
|
+
if (geometry === null || geometry === undefined) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const position_attribute = geometry.getAttribute('position');
|
|
260
|
+
if (position_attribute === undefined) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const positions = position_attribute.array;
|
|
265
|
+
const indices = geometry_indices(geometry, position_attribute.count);
|
|
266
|
+
|
|
267
|
+
const mesh = new BinaryTopology();
|
|
268
|
+
bt_mesh_from_indexed_geometry(mesh, indices, positions);
|
|
269
|
+
|
|
270
|
+
const { charts } = atlas_compute_charts(mesh, { weld: true });
|
|
271
|
+
if (charts.length === 0) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const matrix = transform.matrix;
|
|
276
|
+
const normal_matrix = new Float32Array(9);
|
|
277
|
+
m4_normal_matrix3(normal_matrix, matrix);
|
|
278
|
+
|
|
279
|
+
const area_factor = mat4_mean_scale(matrix) ** 2;
|
|
280
|
+
for (let i = 0; i < charts.length; i++) {
|
|
281
|
+
charts[i].surface_area *= area_factor;
|
|
282
|
+
all_charts.push(charts[i]);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// keep the source normals/indices to carry continuous shading normals
|
|
286
|
+
// across chart seams (see lightmap_build_geometry)
|
|
287
|
+
const normal_attribute = geometry.getAttribute('normal');
|
|
288
|
+
const source_normals = normal_attribute !== undefined ? normal_attribute.array : null;
|
|
289
|
+
|
|
290
|
+
entries.push({ sg, matrix, normal_matrix, charts, source_normals, source_indices: indices });
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (all_charts.length === 0) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// pack every chart from every mesh into one shared atlas. atlas_pack_charts
|
|
298
|
+
// is mesh-free (works purely at the Chart level), so charts from all meshes
|
|
299
|
+
// pack together and each chart's `uv` is rewritten into shared-atlas space.
|
|
300
|
+
atlas_pack_charts(all_charts, {
|
|
301
|
+
maxResolution: resolution,
|
|
302
|
+
maxPages: 1,
|
|
303
|
+
padding,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const jx = [];
|
|
307
|
+
const jy = [];
|
|
308
|
+
const jp = [];
|
|
309
|
+
const jn = [];
|
|
310
|
+
const jt = [];
|
|
311
|
+
|
|
312
|
+
const local_position = new Float32Array(3);
|
|
313
|
+
const local_normal = new Float32Array(3);
|
|
314
|
+
const world_position = new Float32Array(3);
|
|
315
|
+
const world_normal = new Float32Array(3);
|
|
316
|
+
const wa = new Float32Array(3);
|
|
317
|
+
const wb = new Float32Array(3);
|
|
318
|
+
const wc = new Float32Array(3);
|
|
319
|
+
|
|
320
|
+
for (let e = 0; e < entries.length; e++) {
|
|
321
|
+
const entry = entries[e];
|
|
322
|
+
const built = lightmap_build_geometry(entry.charts, entry.source_normals, entry.source_indices);
|
|
323
|
+
|
|
324
|
+
// swap in the re-charted, lightmap-UV geometry and point the material
|
|
325
|
+
// at the shared lightmap
|
|
326
|
+
entry.sg.geometry = built.geometry;
|
|
327
|
+
chart_groups.push(entry.charts);
|
|
328
|
+
|
|
329
|
+
const material = entry.sg.material;
|
|
330
|
+
if (material !== null && material !== undefined) {
|
|
331
|
+
material.lightMap = texture;
|
|
332
|
+
material.lightMapIntensity = light_map_intensity;
|
|
333
|
+
material.needsUpdate = true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const built_positions = built.positions;
|
|
337
|
+
const built_normals = built.normals;
|
|
338
|
+
const offsets = built.chart_vertex_offset;
|
|
339
|
+
const matrix = entry.matrix;
|
|
340
|
+
const normal_matrix = entry.normal_matrix;
|
|
341
|
+
const charts = entry.charts;
|
|
342
|
+
|
|
343
|
+
for (let ci = 0; ci < charts.length; ci++) {
|
|
344
|
+
const chart = charts[ci];
|
|
345
|
+
const base = offsets[ci];
|
|
346
|
+
const tri_local = chart.tri_local;
|
|
347
|
+
const uv = chart.uv;
|
|
348
|
+
const triangle_count = chart.triangle_count;
|
|
349
|
+
|
|
350
|
+
for (let t = 0; t < triangle_count; t++) {
|
|
351
|
+
const la = tri_local[t * 3];
|
|
352
|
+
const lb = tri_local[t * 3 + 1];
|
|
353
|
+
const lc = tri_local[t * 3 + 2];
|
|
354
|
+
|
|
355
|
+
const ga = base + la;
|
|
356
|
+
const gb = base + lb;
|
|
357
|
+
const gc = base + lc;
|
|
358
|
+
|
|
359
|
+
// world-space size of one lightmap texel for this triangle =
|
|
360
|
+
// sqrt(worldArea / texelArea); used by the gather for the
|
|
361
|
+
// confidence-probe reach and sub-texel supersample jitter (both
|
|
362
|
+
// legitimately footprint-sized). skip degenerate (zero-area) triangles.
|
|
363
|
+
const ua = uv[la * 2], va = uv[la * 2 + 1];
|
|
364
|
+
const ub = uv[lb * 2], vb = uv[lb * 2 + 1];
|
|
365
|
+
const uc = uv[lc * 2], vc = uv[lc * 2 + 1];
|
|
366
|
+
const uv_area = 0.5 * Math.abs((ub - ua) * (vc - va) - (vb - va) * (uc - ua));
|
|
367
|
+
const texels_in_tri = uv_area * resolution * resolution;
|
|
368
|
+
if (!(texels_in_tri > 1e-9)) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
v3_matrix4_multiply(wa, 0, built_positions, ga * 3, matrix);
|
|
373
|
+
v3_matrix4_multiply(wb, 0, built_positions, gb * 3, matrix);
|
|
374
|
+
v3_matrix4_multiply(wc, 0, built_positions, gc * 3, matrix);
|
|
375
|
+
const e1x = wb[0] - wa[0], e1y = wb[1] - wa[1], e1z = wb[2] - wa[2];
|
|
376
|
+
const e2x = wc[0] - wa[0], e2y = wc[1] - wa[1], e2z = wc[2] - wa[2];
|
|
377
|
+
const crx = e1y * e2z - e1z * e2y;
|
|
378
|
+
const cry = e1z * e2x - e1x * e2z;
|
|
379
|
+
const crz = e1x * e2y - e1y * e2x;
|
|
380
|
+
const world_area = 0.5 * v3_length(crx, cry, crz);
|
|
381
|
+
const texel_world_size = Math.sqrt(world_area / texels_in_tri);
|
|
382
|
+
if (!(texel_world_size > 0)) {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
lightmap_rasterize_triangle(
|
|
387
|
+
ua * resolution, va * resolution,
|
|
388
|
+
ub * resolution, vb * resolution,
|
|
389
|
+
uc * resolution, vc * resolution,
|
|
390
|
+
resolution, resolution,
|
|
391
|
+
(texel_x, texel_y, wA, wB, wC) => {
|
|
392
|
+
for (let k = 0; k < 3; k++) {
|
|
393
|
+
local_position[k] =
|
|
394
|
+
wA * built_positions[ga * 3 + k] +
|
|
395
|
+
wB * built_positions[gb * 3 + k] +
|
|
396
|
+
wC * built_positions[gc * 3 + k];
|
|
397
|
+
local_normal[k] =
|
|
398
|
+
wA * built_normals[ga * 3 + k] +
|
|
399
|
+
wB * built_normals[gb * 3 + k] +
|
|
400
|
+
wC * built_normals[gc * 3 + k];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
v3_matrix4_multiply(world_position, 0, local_position, 0, matrix);
|
|
404
|
+
v3_matrix3_rotate(world_normal, 0, local_normal[0], local_normal[1], local_normal[2], normal_matrix);
|
|
405
|
+
v3_normalize_array(world_normal, 0, world_normal, 0);
|
|
406
|
+
|
|
407
|
+
// Small WORLD-SPACE off-surface nudge so the first gather ray
|
|
408
|
+
// doesn't self-intersect the originating surface. Deliberately
|
|
409
|
+
// NOT coupled to lightmap resolution / texel size: a texel-scaled
|
|
410
|
+
// offset over-pushes (clips through nearby geometry) on low-res
|
|
411
|
+
// maps, under-pushes (self-occludes) on dense maps, and varies
|
|
412
|
+
// per-triangle under UV distortion. Scaled by coordinate magnitude
|
|
413
|
+
// only to stay above the float32 ULP at large coordinates.
|
|
414
|
+
const mag = Math.max(1, Math.abs(world_position[0]), Math.abs(world_position[1]), Math.abs(world_position[2]));
|
|
415
|
+
const bias = surface_bias * mag;
|
|
416
|
+
|
|
417
|
+
jx.push(texel_x);
|
|
418
|
+
jy.push(texel_y);
|
|
419
|
+
jp.push(
|
|
420
|
+
world_position[0] + world_normal[0] * bias,
|
|
421
|
+
world_position[1] + world_normal[1] * bias,
|
|
422
|
+
world_position[2] + world_normal[2] * bias
|
|
423
|
+
);
|
|
424
|
+
jn.push(world_normal[0], world_normal[1], world_normal[2]);
|
|
425
|
+
jt.push(texel_world_size);
|
|
426
|
+
},
|
|
427
|
+
true // conservative
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const job_count = jx.length;
|
|
434
|
+
job_x = Int32Array.from(jx);
|
|
435
|
+
job_y = Int32Array.from(jy);
|
|
436
|
+
job_position = Float32Array.from(jp);
|
|
437
|
+
job_normal = Float32Array.from(jn);
|
|
438
|
+
job_texel_size = Float32Array.from(jt);
|
|
439
|
+
|
|
440
|
+
// split into coarse (fully baked first) and fine (interpolated or refined)
|
|
441
|
+
const coarse = [];
|
|
442
|
+
const fine = [];
|
|
443
|
+
for (let j = 0; j < job_count; j++) {
|
|
444
|
+
if (is_coarse(job_x[j], job_y[j])) {
|
|
445
|
+
coarse.push(j);
|
|
446
|
+
} else {
|
|
447
|
+
fine.push(j);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
coarse_indices = Int32Array.from(coarse);
|
|
451
|
+
fine_indices = Int32Array.from(fine);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Path-trace one texel; commit it only if confident enough.
|
|
456
|
+
* @param {number} j job index
|
|
457
|
+
* @param {object} cfg gather config (full or reduced sample count)
|
|
458
|
+
*/
|
|
459
|
+
function bake_texel(j, cfg) {
|
|
460
|
+
const p3 = j * 3;
|
|
461
|
+
const confidence = lightmap_gather(
|
|
462
|
+
gather_out,
|
|
463
|
+
job_position[p3], job_position[p3 + 1], job_position[p3 + 2],
|
|
464
|
+
job_normal[p3], job_normal[p3 + 1], job_normal[p3 + 2],
|
|
465
|
+
job_texel_size[j],
|
|
466
|
+
scene, path_tracer, cfg, random
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
const pixel = job_y[j] * resolution + job_x[j];
|
|
470
|
+
if (confidence >= valid_threshold) {
|
|
471
|
+
const texel = pixel * 4;
|
|
472
|
+
atlas.data[texel] = gather_out[0];
|
|
473
|
+
atlas.data[texel + 1] = gather_out[1];
|
|
474
|
+
atlas.data[texel + 2] = gather_out[2];
|
|
475
|
+
atlas.data[texel + 3] = 1;
|
|
476
|
+
variance[pixel] = gather_out[3];
|
|
477
|
+
const n3 = pixel * 3;
|
|
478
|
+
texel_normals[n3] = job_normal[p3];
|
|
479
|
+
texel_normals[n3 + 1] = job_normal[p3 + 1];
|
|
480
|
+
texel_normals[n3 + 2] = job_normal[p3 + 2];
|
|
481
|
+
if (texel_world_pos !== null) {
|
|
482
|
+
texel_world_pos[n3] = job_position[p3];
|
|
483
|
+
texel_world_pos[n3 + 1] = job_position[p3 + 1];
|
|
484
|
+
texel_world_pos[n3 + 2] = job_position[p3 + 2];
|
|
485
|
+
}
|
|
486
|
+
coverage[pixel] = 1;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Luminance of a committed atlas texel.
|
|
492
|
+
* @param {number} pixel
|
|
493
|
+
* @returns {number}
|
|
494
|
+
*/
|
|
495
|
+
function texel_luminance(pixel) {
|
|
496
|
+
const t = pixel * 4;
|
|
497
|
+
return rgb_to_luminance(atlas.data[t], atlas.data[t + 1], atlas.data[t + 2]);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Whether a fine texel sits inside a "smooth" cell: its four surrounding
|
|
502
|
+
* coarse-grid texels are all committed and agree within the threshold.
|
|
503
|
+
* @param {number} j job index
|
|
504
|
+
* @returns {boolean}
|
|
505
|
+
*/
|
|
506
|
+
function smooth_cell(j) {
|
|
507
|
+
const x = job_x[j];
|
|
508
|
+
const y = job_y[j];
|
|
509
|
+
const gx0 = Math.floor(x / adaptive_step) * adaptive_step;
|
|
510
|
+
const gy0 = Math.floor(y / adaptive_step) * adaptive_step;
|
|
511
|
+
const gx1 = gx0 + adaptive_step;
|
|
512
|
+
const gy1 = gy0 + adaptive_step;
|
|
513
|
+
if (gx1 >= resolution || gy1 >= resolution) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const p00 = gy0 * resolution + gx0;
|
|
518
|
+
const p10 = gy0 * resolution + gx1;
|
|
519
|
+
const p01 = gy1 * resolution + gx0;
|
|
520
|
+
const p11 = gy1 * resolution + gx1;
|
|
521
|
+
if (!coverage[p00] || !coverage[p10] || !coverage[p01] || !coverage[p11]) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const l00 = texel_luminance(p00);
|
|
526
|
+
const l10 = texel_luminance(p10);
|
|
527
|
+
const l01 = texel_luminance(p01);
|
|
528
|
+
const l11 = texel_luminance(p11);
|
|
529
|
+
const max_l = Math.max(l00, l10, l01, l11);
|
|
530
|
+
const min_l = Math.min(l00, l10, l01, l11);
|
|
531
|
+
return max_l - min_l <= interpolation_threshold * (max_l + 1e-6);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Fill a fine texel by bilinear interpolation of its four coarse-grid corners
|
|
536
|
+
* (used only when `min_sample_fraction === 0`). Assumes {@link smooth_cell}.
|
|
537
|
+
* @param {number} j job index
|
|
538
|
+
*/
|
|
539
|
+
function interpolate(j) {
|
|
540
|
+
const x = job_x[j];
|
|
541
|
+
const y = job_y[j];
|
|
542
|
+
const gx0 = Math.floor(x / adaptive_step) * adaptive_step;
|
|
543
|
+
const gy0 = Math.floor(y / adaptive_step) * adaptive_step;
|
|
544
|
+
const gx1 = gx0 + adaptive_step;
|
|
545
|
+
const gy1 = gy0 + adaptive_step;
|
|
546
|
+
|
|
547
|
+
const p00 = gy0 * resolution + gx0;
|
|
548
|
+
const p10 = gy0 * resolution + gx1;
|
|
549
|
+
const p01 = gy1 * resolution + gx0;
|
|
550
|
+
const p11 = gy1 * resolution + gx1;
|
|
551
|
+
|
|
552
|
+
const fx = (x - gx0) / adaptive_step;
|
|
553
|
+
const fy = (y - gy0) / adaptive_step;
|
|
554
|
+
const pixel = y * resolution + x;
|
|
555
|
+
const t = pixel * 4;
|
|
556
|
+
for (let c = 0; c < 3; c++) {
|
|
557
|
+
const a = atlas.data[p00 * 4 + c] * (1 - fx) + atlas.data[p10 * 4 + c] * fx;
|
|
558
|
+
const b = atlas.data[p01 * 4 + c] * (1 - fx) + atlas.data[p11 * 4 + c] * fx;
|
|
559
|
+
atlas.data[t + c] = a * (1 - fy) + b * fy;
|
|
560
|
+
}
|
|
561
|
+
atlas.data[t + 3] = 1;
|
|
562
|
+
variance[pixel] = 0;
|
|
563
|
+
const j3 = j * 3;
|
|
564
|
+
const n3 = pixel * 3;
|
|
565
|
+
texel_normals[n3] = job_normal[j3];
|
|
566
|
+
texel_normals[n3 + 1] = job_normal[j3 + 1];
|
|
567
|
+
texel_normals[n3 + 2] = job_normal[j3 + 2];
|
|
568
|
+
if (texel_world_pos !== null) {
|
|
569
|
+
texel_world_pos[n3] = job_position[j3];
|
|
570
|
+
texel_world_pos[n3 + 1] = job_position[j3 + 1];
|
|
571
|
+
texel_world_pos[n3 + 2] = job_position[j3 + 2];
|
|
572
|
+
}
|
|
573
|
+
coverage[pixel] = 1;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Bake one fine texel: a reduced real estimate in smooth cells (or a pure lerp
|
|
578
|
+
* when `min_sample_fraction === 0`), full sampling on edges/discontinuities.
|
|
579
|
+
* @param {number} j job index
|
|
580
|
+
*/
|
|
581
|
+
function bake_fine(j) {
|
|
582
|
+
if (smooth_cell(j)) {
|
|
583
|
+
if (reduced_samples > 0) {
|
|
584
|
+
bake_texel(j, gather_cfg_reduced);
|
|
585
|
+
} else {
|
|
586
|
+
interpolate(j);
|
|
587
|
+
}
|
|
588
|
+
} else {
|
|
589
|
+
bake_texel(j, gather_cfg_full);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function finish() {
|
|
594
|
+
// the committed (baked) set, before dilation grows it into the gutter
|
|
595
|
+
if (debug) {
|
|
596
|
+
baked_coverage = coverage.slice();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (denoise) {
|
|
600
|
+
const filtered = new Float32Array(atlas.data.length);
|
|
601
|
+
lightmap_denoise(filtered, atlas.data, variance, texel_normals, coverage, resolution, resolution, 4);
|
|
602
|
+
atlas.data.set(filtered);
|
|
603
|
+
}
|
|
604
|
+
// grow lighting into the gutter / holes so bilinear sampling at seams and
|
|
605
|
+
// discarded (low-confidence) texels does not read black
|
|
606
|
+
lightmap_dilate(atlas.data, coverage, resolution, resolution, 4, padding + 2);
|
|
607
|
+
// blend the two atlas-space sides of each smooth UV seam so they match
|
|
608
|
+
// (normal-aware: hard creases like a cube edge are left alone)
|
|
609
|
+
if (seam_fix_iterations > 0 && chart_groups.length > 0) {
|
|
610
|
+
lightmap_seam_fix(atlas.data, texel_normals, coverage, resolution, resolution, 4, chart_groups, {
|
|
611
|
+
iterations: seam_fix_iterations,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
texture.needsUpdate = true;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function cycle() {
|
|
618
|
+
if (phase === PHASE_COARSE) {
|
|
619
|
+
if (coarse_cursor >= coarse_indices.length) {
|
|
620
|
+
phase = PHASE_FINE;
|
|
621
|
+
} else {
|
|
622
|
+
const end = Math.min(coarse_cursor + texel_batch, coarse_indices.length);
|
|
623
|
+
for (; coarse_cursor < end; coarse_cursor++) {
|
|
624
|
+
bake_texel(coarse_indices[coarse_cursor], gather_cfg_full);
|
|
625
|
+
}
|
|
626
|
+
texture.needsUpdate = true;
|
|
627
|
+
return TaskSignal.Continue;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (fine_cursor >= fine_indices.length) {
|
|
632
|
+
finish();
|
|
633
|
+
return TaskSignal.EndSuccess;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const end = Math.min(fine_cursor + texel_batch, fine_indices.length);
|
|
637
|
+
for (; fine_cursor < end; fine_cursor++) {
|
|
638
|
+
bake_fine(fine_indices[fine_cursor]);
|
|
639
|
+
}
|
|
640
|
+
texture.needsUpdate = true;
|
|
641
|
+
|
|
642
|
+
if (fine_cursor >= fine_indices.length) {
|
|
643
|
+
finish();
|
|
644
|
+
return TaskSignal.EndSuccess;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return TaskSignal.Continue;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const task = new Task({
|
|
651
|
+
name: `bake lightmap ${resolution}x${resolution}`,
|
|
652
|
+
initializer: prepare,
|
|
653
|
+
cycleFunction: cycle,
|
|
654
|
+
computeProgress() {
|
|
655
|
+
const total = coarse_indices.length + fine_indices.length;
|
|
656
|
+
return total === 0 ? 0 : (coarse_cursor + fine_cursor) / total;
|
|
657
|
+
},
|
|
658
|
+
estimatedDuration: 60,
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const group = new TaskGroup([task], 'lightmap bake');
|
|
662
|
+
|
|
663
|
+
// expose the baked artifacts for inspection / debugging
|
|
664
|
+
group.lightmap = {
|
|
665
|
+
texture,
|
|
666
|
+
sampler: atlas,
|
|
667
|
+
scene,
|
|
668
|
+
coverage,
|
|
669
|
+
variance,
|
|
670
|
+
normals: texel_normals,
|
|
671
|
+
world_pos: texel_world_pos,
|
|
672
|
+
get baked_coverage() {
|
|
673
|
+
return baked_coverage;
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
return group;
|
|
678
|
+
}
|