# The contents of this file are subject to the Common Public Attribution License # Version 1.0 (the “License”); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # https://myou.dev/licenses/LICENSE-CPAL. The License is based on the Mozilla # Public License Version 1.1 but Sections 14 and 15 have been added to cover use # of software over a computer network and provide for limited attribution for # the Original Developer. In addition, Exhibit A has been modified to be # consistent with Exhibit B. # # Software distributed under the License is distributed on an “AS IS” basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for # the specific language governing rights and limitations under the License. # # The Original Code is Myou Engine. # # the Original Developer is the Initial Developer. # # The Initial Developer of the Original Code is the Myou Engine developers. # All portions of the code written by the Myou Engine developers are Copyright # (c) 2024. All Rights Reserved. # # Alternatively, the contents of this file may be used under the terms of the # GNU Affero General Public License version 3 (the [AGPL-3] License), in which # case the provisions of [AGPL-3] License are applicable instead of those above. # # If you wish to allow use of your version of this file only under the terms of # the [AGPL-3] License and not to allow others to use your version of this file # under the CPAL, indicate your decision by deleting the provisions above and # replace them with the notice and other provisions required by the [AGPL-3] # License. If you do not delete the provisions above, a recipient may use your # version of this file under either the CPAL or the [AGPL-3] License. import ../types import std/strformat import vmath except Quat when defined(nimdoc): type TYPES* = ProbeInfluenceType | ProbeParallaxType | CubemapProbe | CubemapProbeUniform | SH9Uniform # Forward declarations and ob type methods proc newCubemapProbe*(engine: MyouEngine, name: string="camera", scene: Scene = nil, influence_type: ProbeInfluenceType = SphereInfluence, influence_distance: float32 = 2.5, falloff: float32 = 0.2, intensity: float32 = 1, clipping_start: float32 = 0.8, clipping_end: float32 = 40, parallax_type: ProbeParallaxType = NoParallax, parallax_distance: float32 = 0.0, # 0 means auto ): CubemapProbe func getCubemapSideMatrix*(side: int, position = vec3()): Mat4 proc render_cubemap*(self: CubemapProbe, use_roughness_prefiltering = false, mipmap_shader: Material = nil) proc render_background_cubemap*(scene: Scene, use_roughness_prefiltering = false, mipmap_shader: Material = nil, world_to_cube_matrix: Mat4 = mat4(), upload_UBO = true) method is_cubemap_probe*(self: GameObject): bool {.base.} = return false method get_cubemap_probe*(self: GameObject): CubemapProbe {.base.} = return nil method is_cubemap_probe*(self: CubemapProbe): bool = return true method get_cubemap_probe*(self: CubemapProbe): CubemapProbe = return self # End forward declarations and ob type methods import ./gameobject import ../postprocessing/effect_shaders import ../graphics/framebuffer import ../graphics/render import ../scene import ../graphics/texture import ../graphics/ubo import arr_ref proc newCubemapProbe*(engine: MyouEngine, name: string="camera", scene: Scene = nil, influence_type: ProbeInfluenceType = SphereInfluence, influence_distance: float32 = 2.5, falloff: float32 = 0.2, intensity: float32 = 1, clipping_start: float32 = 0.8, clipping_end: float32 = 40, parallax_type: ProbeParallaxType = NoParallax, parallax_distance: float32 = 0.0, # 0 means auto ): CubemapProbe = var self = new CubemapProbe discard procCall(self.GameObject.initGameObject(engine, name)) self.ubo_index = -1 self.influence_type = influence_type self.influence_distance = influence_distance self.falloff = falloff self.intensity = intensity self.clipping_start = clipping_start self.clipping_end = clipping_end self.parallax_type = parallax_type self.parallax_distance = if parallax_distance != 0: parallax_distance else: influence_distance self.update_strategy = UpdateOnFirstUse return self func getCubemapSideMatrix*(side: int, position = vec3()): Mat4 = const CUBEMAP_DIRECTIONS = [ vec3(1, 0, 0), vec3(-1, 0, 0), vec3(0, -1, 0), # NOTE: Y is reversed! because cubemaps vec3(0, 1, 0), # have left handed coordinates vec3(0, 0, 1), vec3(0, 0, -1), ] const CUBEMAP_UP_VECTORS = [ vec3(0, -1, 0), vec3(0, -1, 0), vec3(0, 0, -1), # reversed here too vec3(0, 0, 1), # vec3(0, -1, 0), vec3(0, -1, 0), ] let dir = CUBEMAP_DIRECTIONS[side] let up = CUBEMAP_UP_VECTORS[side] let side = cross(up,-dir) return mat4( vec4(side, position.x), vec4(up, position.y), vec4(-dir, position.z), vec4(0,0,0,1)) template getCubemapSideAxis*(side: int): int = side div 2 const MIN_ROUGHNESS_LOD = 1 var roughness_prefilter: Material = nil proc make_roughness_prefilter(engine: MyouEngine) = roughness_prefilter = engine.newEffectShader(@["cube"], texture_types = @[TexCube], library=staticRead("../shaders/cube_prefilter.glsl"), code = &""" float roughness = inputs[0].lod/(inputs[0].px_lod-{MIN_ROUGHNESS_LOD.float32}); outColor = vec4(PrefilterEnvMap(cube, roughness, normalize(coord3D)), 1.0); """, ) template get_roughness_prefilter(engine: MyouEngine): Material = if roughness_prefilter == nil: make_roughness_prefilter(engine) roughness_prefilter proc render_cubemap*(self: CubemapProbe, use_roughness_prefiltering = false, mipmap_shader: Material = nil) = if self.ubo_index == -1: self.scene.ensure_cubemaps() let cube2world = self.world_matrix * scale(vec3(self.influence_distance)) let world2cube = cube2world.inverse if not defined(release): assert self.ubo_index < self.scene.max_cubemaps, "max_cubemaps is too low, make sure you call calculate_max_lights_and_cubemaps()" # self.scene.cubemap_UBO.storage(CubemapProbeUniform)[self.ubo_index] = CubemapProbeUniform( self.scene.cubemap_UBO.byte_storage.to(CubemapProbeUniform)[self.ubo_index] = CubemapProbeUniform( world2cube: world2cube, resolution: self.cubemap.width.float32, # resolution_inverse_squared: (1/(self.cubemap.width^2)).float32, falloff_inv: 1/max(0.0001, self.falloff), intensity: self.intensity, influence_type: self.influence_type.float32, parallax_type: self.parallax_type.float32, par_dist_rel_inv: 1 / (self.parallax_distance / self.influence_distance), roughness_lod: (self.cubemap.texture.mipmapHigh - MIN_ROUGHNESS_LOD).float32, ) self.engine.renderer.draw_cubemap(self.scene, self.cubemap, cube2world, world2cube, self.clipping_start, self.clipping_end, false) let mipmap_shader = if use_roughness_prefiltering and mipmap_shader == nil: self.engine.get_roughness_prefilter() else: mipmap_shader self.cubemap.generate_mipmap(mipmap_shader) self.cubemap.disable() proc render_background_cubemap*(scene: Scene, use_roughness_prefiltering = false, mipmap_shader: Material = nil, world_to_cube_matrix: Mat4 = mat4(), upload_UBO = true) = if scene.background_cubemap == nil: scene.ensure_cubemaps() if not defined(release): assert 0 < scene.max_cubemaps, "max_cubemaps is too low, make sure you call calculate_max_lights_and_cubemaps()" scene.cubemap_UBO.storage(CubemapProbeUniform)[0] = CubemapProbeUniform( world2cube: world_to_cube_matrix, resolution: scene.background_cubemap.width.float32, # resolution_inverse_squared: (1/(scene.background_cubemap.width^2)).float32, falloff_inv: 1, intensity: 1, parallax_type: -1, roughness_lod: (scene.background_cubemap.texture.mipmapHigh - MIN_ROUGHNESS_LOD).float32, ) scene.engine.renderer.draw_cubemap(scene, scene.background_cubemap, mat4(), mat4(), 1, 100, true) let mipmap_shader = if use_roughness_prefiltering and mipmap_shader == nil: scene.engine.get_roughness_prefilter() else: mipmap_shader scene.background_cubemap.generate_mipmap(mipmap_shader) if upload_UBO: scene.cubemap_UBO.update() proc generate_cubemap_mipmaps*(scene: Scene, cubemap: Framebuffer, use_roughness_prefiltering = false, mipmap_shader: Material = nil) = let mipmap_shader = if use_roughness_prefiltering and mipmap_shader == nil: scene.engine.get_roughness_prefilter() else: mipmap_shader cubemap.generate_mipmap(mipmap_shader)