using System; using ComponentFramework.Core; using MTV3D65; namespace ComponentFrameworkTests.Components { [AutoLoad] class PlaneReflection : Component { const float FresnelPower = 5; const float MinReflection = 0.2f; const float MaxReflection = 1; // This defines the height and normal of the reflective plane readonly TV_PLANE WaterPlane = new TV_PLANE(new TV_3DVECTOR(0, 1, 0), 0); TVMesh cube; TVMesh floor; TVRenderSurface renderSurface; TVShader projectionShader; TV_3DMATRIX reflectedView; TVCamera reflectedCamera; public PlaneReflection(ICore core) : base(core) { } public override void Initialize() { // Always better with a light... LightEngine.SetGlobalAmbient(0, 0, 0); var light = new TV_LIGHT { direction = new TV_3DVECTOR(-0.5f, -1, 0.25f), diffuse = new TV_COLOR(1, 1, 1, 1), ambient = new TV_COLOR(1, 1, 1, 1), range = 1000, type = CONST_TV_LIGHTTYPE.TV_LIGHT_DIRECTIONAL }; LightEngine.EnableLight(LightEngine.CreateLight(ref light), true); Atmosphere.SkyBox_Enable(true); // My cube face ordering is either screwed up, or TV3D has the wrong names for them... too lazy to investigate Atmosphere.SkyBox_SetTexture( TextureFactory.LoadTexture("Clouds_NegX.dds"), TextureFactory.LoadTexture("Clouds_PosX.dds"), TextureFactory.LoadTexture("Clouds_NegZ.dds"), TextureFactory.LoadTexture("Clouds_PosZ.dds"), TextureFactory.LoadTexture("Clouds_PosY.dds"), TextureFactory.LoadTexture("Clouds_NegY.dds")); Atmosphere.SkyBox_SetDepthWrite(false); // Easier to manage with a separate camera for the reflected view reflectedCamera = CameraFactory.CreateCamera(); var clipPlane = new TV_PLANE(WaterPlane.Normal, WaterPlane.Dist); reflectedCamera.SetProjectionClipPlane(true, clipPlane); // Fullscreen render surface with the associated camera renderSurface = Scene.CreateRenderSurfaceEx(-1, -1, CONST_TV_RENDERSURFACEFORMAT.TV_TEXTUREFORMAT_A8R8G8B8, true, true, 1); renderSurface.SetNewCamera(reflectedCamera); // Sadly, we need a shader for the texture projection projectionShader = Scene.CreateShader(); projectionShader.CreateFromEffectFile("Projection.fx"); projectionShader.SetEffectParamFloat("FresnelPower", FresnelPower); projectionShader.SetEffectParamFloat("MinReflection", MinReflection); projectionShader.SetEffectParamFloat("MaxReflection", MaxReflection); // Rotating cube get! var defaultMat = MaterialFactory.CreateMaterialQuick(0.9f, 0.9f, 0.9f, 1, ""); MaterialFactory.SetAmbient(defaultMat, 0.1f, 0.1f, 0.1f, 1); MaterialFactory.SetEmissive(defaultMat, 0, 0, 0, 1); cube = Scene.CreateMeshBuilder(); cube.CreateBox(10, 10, 10); cube.SetLightingMode(CONST_TV_LIGHTINGMODE.TV_LIGHTING_MANAGED); cube.SetMaterial(defaultMat); cube.SetPosition(0, 7, 0); // The floor mesh will host both layers : reflection and texture floor = Scene.CreateMeshBuilder(); // The first floor group will be the actual floor, with any texture and rendered with TV built-in shaders or the FFP floor.AddFloor(TextureFactory.LoadTexture("Checkerboard.png"), -0.5f, -0.5f, 0.5f, 0.5f, 0, 5, 5); floor.SetMaterial(defaultMat, 0); floor.SetAlphaTest(false, -1, false, 0); // The second group is the reflection floor.AddFloor(renderSurface.GetTexture(), -0.5f, -0.5f, 0.5f, 0.5f, 0); floor.SetShaderEx(projectionShader, false, 1); floor.SetLightingMode(CONST_TV_LIGHTINGMODE.TV_LIGHTING_MANAGED); floor.ComputeNormals(); var position = WaterPlane.Normal * WaterPlane.Dist; floor.SetPosition(position.x, position.y, position.z); floor.SetScale(100, 1, 100); } public override void Update(TimeSpan elapsedTime) { // Move it to show the reflection's dynamic cube.RotateY((float) elapsedTime.TotalSeconds); cube.RotateX(0.7f * (float)elapsedTime.TotalSeconds); } public override void PreDraw() { // Dual-reflection setup, the best way I know to render inverted views // The second reflection allows to use the same culling mode, otherwise you'd have to render everything doublesided var viewportCamera = Core.Engine.GetCamera(); var reflView = viewportCamera.GetRotationMatrix(); var camPos = viewportCamera.GetPosition(); reflView.m41 = camPos.x; reflView.m42 = camPos.y; reflView.m43 = camPos.z; MathLibrary.TVMatrixReflect(ref reflectedView, WaterPlane); reflView *= reflectedView; var comb = new TV_PLANE(); var vec = reflView.RowVec3(3); var vec2 = vec + reflView.RowVec3(2); var vec3 = vec + reflView.RowVec3(1); MathLibrary.TVPlaneFromPoints(ref comb, vec, vec2, vec3); MathLibrary.TVMatrixReflect(ref reflectedView, comb); reflView *= reflectedView; reflectedCamera.SetMatrix(reflView); renderSurface.StartRender(); { Atmosphere.SkyBox_Render(); cube.Render(); } renderSurface.EndRender(); } public override void Draw() { Atmosphere.SkyBox_Render(); cube.Render(); floor.Render(); } } }