from ... import ShaderMaterial from ... import ReadFile from mathutils import Vector from mathutils import Matrix import time import math import bge import bgl class Water(ShaderMaterial): '''The original work for this material comes from Martinsh: > http://devlog-martinsh.blogspot.ca/''' FragmentShader = ReadFile('./water.fs', __file__) VertexShader = ReadFile('./water.vs', __file__) # Parameters backgroundColor = Vector((.1, .2, .8, .0)) waterCamera = property( lambda self: self.owner.children.get('WaterRenderCamera')) activeCamera = property( lambda self: self.owner.scene.active_camera) height = property( lambda self: bge.render.getWindowHeight()) width = property( lambda self: bge.render.getWindowWidth()) reflectionSize = 512 refractionSize = 512 timeModulo = 1 << 16 offset = -0.2 def init(self): # Setting up the reflection texture self.reflection = bge.texture.Texture(self.owner, 0, 0) self.reflection.source = bge.texture.ImageRender( self.owner.scene, self.waterCamera) self.reflection.source.capsize = ( self.width, self.height) # self.reflectionSize, self.reflectionSize) # self.reflectionSize // 2, self.reflectionSize // 2) self.reflection.source.background = list( 255 * self.backgroundColor) self.reflection.refresh(True) # Setting up the refraction texture self.refraction = bge.texture.Texture(self.owner, 0, 1) self.refraction.source = bge.texture.ImageRender( self.owner.scene, self.waterCamera) self.refraction.source.capsize = ( self.width, self.height) # self.refractionSize, self.refractionSize) # self.refractionSize // 2, self.refractionSize // 2) self.refraction.source.background = list( 255 * self.backgroundColor) self.refraction.refresh(True) def pre_draw_setup(self, shader): '''I have no idea why, but this has to run on pre_draw_setup...''' shader.setAttrib(bge.logic.SHD_TANGENT) shader.setUniformDef('ModelMatrix', bge.logic.MODELMATRIX) shader.setUniformDef('cameraPos', bge.logic.CAM_POS) shader.setSampler('reflectionSampler', 0) shader.setSampler('refractionSampler', 1) shader.setSampler('normalSampler', 2) shader.setSampler('depthSampler', 3) shader.setUniform1f('timer', (3 * time.time()) % self.timeModulo) # User uniforms shader.setUniform1f('scale', self.owner.get('water.scale', 1.)) shader.setUniform1f('windSpeed', self.owner.get('water.wind.speed', .2)) self.watertexture() def watertexture(self): waterCamera = self.waterCamera activeCamera = self.activeCamera # Setting up the water camera parameters waterCamera.lens = activeCamera.lens waterCamera.projection_matrix = activeCamera.projection_matrix self.owner.visible = False # plane normals Z = Front normal = self.owner.getAxisVect((0., 0., 1.)) # closest distance from center to plane distance = -self.owner.position.project(normal).magnitude # VdotN to get front-face/back-face V = (activeCamera.position - self.owner.position ).normalized().dot(normal) # Invert normals when back-face (?) if V < 0: normal = -normal # Processing the reflection and the refraction self.processReflection(activeCamera, waterCamera, normal, distance) self.processRefraction(activeCamera, waterCamera, normal, distance) self.owner.visible = True def processReflection(self, activeCamera, waterCamera, normal, distance): '''This is the refactored way of processing reflection''' M1 = Matrix(self.owner.orientation) M2 = Matrix(self.owner.orientation) M2.invert() R = Matrix.Rotation(math.radians(180), 3, 'Y') U = Matrix.Scale(-1, 3, Vector((1, 0, 0))) position = (activeCamera.position - self.owner.position) * M1 position = self.owner.position + position * R * U * M2 orientation = Matrix(activeCamera.orientation) orientation.transpose() orientation = orientation * M1 * R * U * M2 orientation.transpose() # Orienting and positioning the water camera waterCamera.orientation = orientation waterCamera.position = position # Culling front faces as the camera is scaled to -1 (?) bgl.glCullFace(bgl.GL_FRONT) # Changing clipping plane plane = bgl.Buffer(bgl.GL_DOUBLE, [4], ( -normal[0], -normal[1], -normal[2], -distance + self.offset)) bgl.glClipPlane(bgl.GL_CLIP_PLANE0, plane) bgl.glEnable(bgl.GL_CLIP_PLANE0) self.reflection.refresh(True) # Reverting parameters bgl.glCullFace(bgl.GL_BACK) bgl.glDisable(bgl.GL_CLIP_PLANE0) def processRefraction(self, activeCamera, waterCamera, normal, distance): '''This is the refactored way of processing refraction''' orientation = Matrix(activeCamera.orientation) position = activeCamera.position # Orienting and positioning the water camera waterCamera.orientation = orientation waterCamera.position = position # Changing clipping plane plane = bgl.Buffer(bgl.GL_DOUBLE, [4], ( normal[0], normal[1], normal[2], -distance + self.offset)) bgl.glClipPlane(bgl.GL_CLIP_PLANE1, plane) bgl.glEnable(bgl.GL_CLIP_PLANE1) self.refraction.refresh(True) bgl.glDisable(bgl.GL_CLIP_PLANE1)