public void updateCamera(ref Matrix4 rendererWorldMat, ref Vector3 cameraPosLocal) { var laserParams = _laser.parameters; for (int i = 0; i < laserParams.numBeams; ++i) { var beam = _laser.beam(i); var flashEmitter = _flashEmitters [i]; var smokeEmitter = _smokeEmitters [i]; // TODO need intersection location if (beam.hitsAnObstacle) { var hitPosLocal = Vector3.Transform(beam.endPosWorld, rendererWorldMat.Inverted()); var towardsCamera = (cameraPosLocal - hitPosLocal).Normalized(); flashEmitter.center = hitPosLocal; flashEmitter.up = towardsCamera; flashEmitter.particlesPerEmissionMin = laserParams.flashParticlesPerEmissionMin; flashEmitter.particlesPerEmissionMax = laserParams.flashParticlesPerEmissionMax; smokeEmitter.center = hitPosLocal; smokeEmitter.up = towardsCamera; smokeEmitter.particlesPerEmissionMin = laserParams.flameSmokeParticlesPerEmissionMin; smokeEmitter.particlesPerEmissionMax = laserParams.flameSmokeParticlesPerEmissionMax; } else { // no hit = no particle emissions flashEmitter.particlesPerEmission = 0; smokeEmitter.particlesPerEmission = 0; } } _flamesSmokeColorEffector.colorMask = _laser.parameters.backgroundColor; _flamesSmokeColorEffector.colorMask.A = _laser.envelopeIntensity; _flashColorEffector.colorMask = _laser.parameters.overlayColor; _flashColorEffector.colorMask.A = _laser.envelopeIntensity; }
public override void Render(SSRenderConfig renderConfig) { var beam = _laser.beam(_beamId); if (beam == null) { return; } base.Render(renderConfig); // step: setup render settings SSShaderProgram.DeactivateAll(); GL.ActiveTexture(TextureUnit.Texture0); GL.Enable(EnableCap.Texture2D); var laserParams = _laser.parameters; var startView = Vector3.Transform(beam.startPosWorld, renderConfig.invCameraViewMatrix); var endView = Vector3.Transform(beam.endPosWorld, renderConfig.invCameraViewMatrix); var middleView = (startView + endView) / 2f; // step: draw middle section: Vector3 diff = endView - startView; float diff_xy = diff.Xy.Length; float phi = -(float)Math.Atan2(diff.Z, diff_xy); float theta = (float)Math.Atan2(diff.Y, diff.X); Matrix4 backgroundOrientMat = Matrix4.CreateRotationY(phi) * Matrix4.CreateRotationZ(theta); Matrix4 middlePlacementMat = backgroundOrientMat * Matrix4.CreateTranslation(middleView); //Matrix4 startPlacementMat = Matrix4.CreateTranslation (startView); float laserLength = diff.Length; float middleWidth = laserParams.middleBackgroundWidth * _laser.envelopeIntensity; Vector3 cameraDir = Vector3.Transform( -Vector3.UnitZ, _cameraScene.renderConfig.invCameraViewMatrix).Normalized(); float dot = Vector3.Dot(cameraDir, beam.directionWorld()); dot = Math.Max(dot, 0f); float interferenceWidth = middleWidth * laserParams.middleInterferenceScale; GL.Color4(1f, 1f, 1f, beam.periodicIntensity * beam.periodicIntensity); _updateMiddleMesh(laserLength, middleWidth); #if true // stretched middle background sprite if (middleBackgroundSprite != null) { GL.Material(MaterialFace.Front, MaterialParameter.Emission, laserParams.backgroundColor); GL.BindTexture(TextureTarget.Texture2D, middleBackgroundSprite.TextureID); foreach (var oriX in xOrientationPresets) { Matrix4 rotated = oriX * middlePlacementMat; GL.LoadMatrix(ref rotated); _middleMesh.renderMesh(renderConfig); } } #endif #if true // stretched middle overlay sprite if (middleOverlayTexture != null) { GL.Material(MaterialFace.Front, MaterialParameter.Emission, laserParams.overlayColor); GL.BindTexture(TextureTarget.Texture2D, middleOverlayTexture.TextureID); _middleMesh.renderMesh(renderConfig); foreach (var oriX in xOrientationPresets) { Matrix4 rotated = oriX * middlePlacementMat; GL.LoadMatrix(ref rotated); _middleMesh.renderMesh(renderConfig); } } #endif #if true // interference sprite with a moving U-coordinate offset if (laserParams.middleInterferenceScale > 0f && interferenceTexture != null) { _updateInterfernenceVertices(laserLength, interferenceWidth); GL.Material(MaterialFace.Front, MaterialParameter.Emission, laserParams.middleInterferenceColor); //GL.BindTexture(TextureTarget.Texture2D, interferenceSprite.TextureID); GL.BindTexture(TextureTarget.Texture2D, interferenceTexture.TextureID); var scaleMat = Matrix4.CreateScale(laserLength + middleWidth / 2f, interferenceWidth, 1f); foreach (var oriX in xOrientationPresets) { Matrix4 rotated = scaleMat * oriX * middlePlacementMat; GL.LoadMatrix(ref rotated); _interferenceMesh.renderMesh(renderConfig); } } #endif }
public void updateSprites(SInstancedSpriteData instanceData, ref RectangleF clientRect, ref Vector3 cameraPos, ref Matrix4 camera3dView, ref Matrix4 camera3dProj) { var beam = _laser.beam(_beamId); var ray = beam.rayWorld(); var laserParams = _laser.parameters; var beamSrcInViewSpace = Vector3.Transform(beam.startPosWorld, camera3dView); bool hideSprites = true; Vector3 intersectPt3d; if (beamSrcInViewSpace.Z < 0f) { Matrix4 cameraViewProjMat3d = camera3dView * camera3dProj; // extract a near plane from the camera view projection matrix // https://www.opengl.org/discussion_boards/showthread.php/159332-Extract-clip-planes-from-projection-matrix SSPlane3d nearPlane = new SSPlane3d() { A = cameraViewProjMat3d.M14 + cameraViewProjMat3d.M13, B = cameraViewProjMat3d.M24 + cameraViewProjMat3d.M23, C = cameraViewProjMat3d.M34 + cameraViewProjMat3d.M33, D = cameraViewProjMat3d.M44 + cameraViewProjMat3d.M43 }; if (nearPlane.intersects(ref ray, out intersectPt3d)) { #if false Console.WriteLine("camera at world pos = " + cameraPos); Console.WriteLine("camera in screen pos = " + OpenTKHelper.WorldToScreen( cameraPos, ref cameraViewProjMat3d, ref clientRect)); Console.WriteLine("screen hit at world pos = " + intersectPt3d); #endif float lengthToIntersectionSq = (intersectPt3d - beam.startPosWorld).LengthSquared; float beamLengthSq = beam.lengthSqWorld(); if (lengthToIntersectionSq < beamLengthSq) { hideSprites = false; Vector2 drawScreenPos = OpenTKHelper.WorldToScreen( intersectPt3d - ray.dir * 1f, ref cameraViewProjMat3d, ref clientRect); //Console.WriteLine("screen hit at screen pos = " + drawScreenPos); float intensity = _laser.envelopeIntensity * beam.periodicIntensity; Vector2 drawScale = new Vector2(laserParams.hitFlareSizeMaxPx * (float)Math.Exp(intensity)); for (int i = 0; i < _spriteSlotIdxs.Length; ++i) { int writeIdx = _spriteSlotIdxs [i]; instanceData.writePosition(writeIdx, drawScreenPos); instanceData.writeComponentScale(writeIdx, drawScale); instanceData.writeOrientationZ(writeIdx, intensity * 2f * (float)Math.PI); } Color4 backgroundColor = laserParams.backgroundColor; backgroundColor.A = intensity; instanceData.writeColor(_spriteSlotIdxs[(int)SpriteId.coronaBackground], backgroundColor); Color4 overlayColor = laserParams.overlayColor; //overlayColor.A = intensity / _laser.parameters.intensityEnvelope.sustainLevel; overlayColor.A = Math.Min(intensity * 2f, 1f); instanceData.writeColor(_spriteSlotIdxs[(int)SpriteId.coronaOverlay], overlayColor); //System.Console.WriteLine("overlay.alpha == " + overlayColor.A); Color4 ring1Color = laserParams.overlayColor; //ring1Color.A = (float)Math.Pow(intensity, 5.0); ring1Color.A = 0.1f * intensity; instanceData.writeComponentScale(_spriteSlotIdxs[(int)SpriteId.ring1], drawScale * (float)Math.Exp(intensity)); instanceData.writeColor(_spriteSlotIdxs[(int)SpriteId.ring1], ring1Color); //instanceData.writeColor(_spriteSlotIdxs[(int)SpriteId.ring1], Color4.LimeGreen); Color4 ring2Color = laserParams.backgroundColor; //ring2Color.A = (float)Math.Pow(intensity, 10.0); ring2Color.A = intensity * 0.1f; instanceData.writeColor(_spriteSlotIdxs[(int)SpriteId.ring2], ring2Color); //instanceData.writeColor(_spriteSlotIdxs[(int)SpriteId.ring2], Color4.Magenta); } } } if (hideSprites) { // hide sprites for (int i = 0; i < _spriteSlotIdxs.Length; ++i) { instanceData.writeComponentScale(_spriteSlotIdxs[i], Vector2.Zero); } } //System.Console.WriteLine("beam id " + _beamId + " hitting screen at xy " + hitPosOnScreen); }
public void updateSprites(SInstancedSpriteData instanceData, ref RectangleF screenClientRect, ref Vector3 cameraPos, ref Matrix4 camera3dView, ref Matrix4 camera3dProj) { float occDiskScreenAreaUsed = (float)_occDiskObj.OcclusionQueueryResult; if (occDiskScreenAreaUsed <= 0f) { // hide all sprites var nanVec = new Vector2(float.NaN); instanceData.writePosition(_backgroundSpriteIdx, nanVec); instanceData.writePosition(_overlaySpriteIdx, nanVec); return; } var laserParams = _laser.parameters; var beam = _laser.beam(_beamId); float beamIntensity = _laser.envelopeIntensity * beam.periodicIntensity; // position sprites at the beam start in screen space Matrix4 camera3dViewProjMat = camera3dView * camera3dProj; var beamStartScreen = OpenTKHelper.WorldToScreen(beam.startPosWorld, ref camera3dViewProjMat, ref screenClientRect); instanceData.writePosition(_backgroundSpriteIdx, beamStartScreen); instanceData.writePosition(_overlaySpriteIdx, beamStartScreen); // compute screen space needed to occupy the area where the start of the middle's crossbeam // would be displayed Matrix4 viewInverted = camera3dView.Inverted(); Vector3 viewRight = Vector3.Transform(Vector3.UnitX, viewInverted).Normalized(); Vector3 occRightMost = _occDiskObj.Pos + viewRight * laserParams.middleBackgroundWidth; Vector2 occRightMostPt = OpenTKHelper.WorldToScreen(occRightMost, ref camera3dViewProjMat, ref screenClientRect); Vector2 occCenterPt = OpenTKHelper.WorldToScreen(_occDiskObj.Pos, ref camera3dViewProjMat, ref screenClientRect); float crossBeamRadiusScreenPx = Math.Abs(occRightMostPt.X - occCenterPt.X); // write sprite size big enough to cover up the starting section of the cross beam (middle) float scale = Math.Max(laserParams.emissionFlareScreenSizeMin, crossBeamRadiusScreenPx * 2.5f) * beamIntensity; instanceData.writeMasterScale(_backgroundSpriteIdx, scale * 1.2f); instanceData.writeMasterScale(_overlaySpriteIdx, scale * 1f); // add some variety to orientation to make the sprites look less static instanceData.writeOrientationZ(_backgroundSpriteIdx, beamIntensity * 1f * (float)Math.PI); instanceData.writeOrientationZ(_overlaySpriteIdx, beamIntensity * 1f * (float)Math.PI); // color intensity: depends on the dot product between to-camera vector and beam direction; // also depends on how of the occlusion disk area is visible float maxScreenArea = (float)Math.PI * laserParams.emissionOccDiskRadiusPx * laserParams.emissionOccDiskRadiusPx; float occDiskAreaRatio = occDiskScreenAreaUsed / maxScreenArea; //System.Console.WriteLine("occDiskAreaRatio = " + occDiskAreaRatio); Vector3 toCamera = (cameraPos - beam.startPosWorld).Normalized(); float dot = Math.Max(0f, Vector3.Dot(toCamera, beam.directionWorld())); //System.Console.WriteLine("dot = " + dot); var alpha = occDiskAreaRatio * 1.2f * (float)Math.Pow(beamIntensity, 0.1) * (float)Math.Pow(dot, 0.1); alpha = Math.Min(alpha, 1f); // finish background color var backgroundColor = laserParams.backgroundColor; backgroundColor.A = alpha; instanceData.writeColor(_backgroundSpriteIdx, backgroundColor); // finish overlay color var overlayColor = laserParams.overlayColor; overlayColor.A = alpha; instanceData.writeColor(_overlaySpriteIdx, overlayColor); }