CNCMaps.Engine.Rendering.VxlRenderer.Render C# (CSharp) Method

Render() public method

public Render ( CNCMaps.FileFormats.VxlFile vxl, CNCMaps.FileFormats.HvaFile hva, GameObject obj, CNCMaps.Engine.Rendering.DrawProperties props ) : CNCMaps.Engine.Rendering.DrawingSurface
vxl CNCMaps.FileFormats.VxlFile
hva CNCMaps.FileFormats.HvaFile
obj CNCMaps.Engine.Map.GameObject
props CNCMaps.Engine.Rendering.DrawProperties
return CNCMaps.Engine.Rendering.DrawingSurface
        public DrawingSurface Render(VxlFile vxl, HvaFile hva, GameObject obj, DrawProperties props)
        {
            if (!_isInit) Initialize();
            if (!_canRender) {
                Logger.Warn("Not rendering {0} because no OpenGL context could be obtained", vxl.FileName);
                return null;
            }

            Logger.Debug("Rendering voxel {0}", vxl.FileName);
            vxl.Initialize();
            hva.Initialize();

            GL.Viewport(0, 0, _surface.BitmapData.Width, _surface.BitmapData.Height);
            GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit);

            // RA2 uses dimetric projection with camera elevated 30° off the ground
            GL.MatrixMode(MatrixMode.Projection);
            var persp = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(30), _surface.BitmapData.Width / (float)_surface.BitmapData.Height, 1, _surface.BitmapData.Height);
            GL.LoadMatrix(ref persp);

            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();

            var lookat = Matrix4.LookAt(0, 0, -10, 0, 0, 0, 0, 1, 0);
            GL.MultMatrix(ref lookat);

            var trans = Matrix4.CreateTranslation(0, 0, 10);
            GL.MultMatrix(ref trans);

            // align and zoom
            var world = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(60));
            world = Matrix4.CreateRotationY(MathHelper.DegreesToRadians(180)) * world;
            world = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(-45)) * world;
            world = Matrix4.Scale(0.028f, 0.028f, 0.028f) * world;
            GL.MultMatrix(ref world);

            // DrawAxes();

            // determine tilt vectors
            Matrix4 tilt = Matrix4.Identity;
            int tiltPitch =0 , tiltYaw = 0;
            if (obj.Tile.Drawable != null) {
                int ramp = (obj.Tile.Drawable as TileDrawable).GetTileImage(obj.Tile).RampType;
                if (ramp == 0 || ramp >= 17) {
                    tiltPitch = tiltYaw = 0;
                }
                else if (ramp <= 4) {
                    // screen-diagonal facings (perpendicular to axes)
                    tiltPitch = 25;
                    tiltYaw = -90 * ramp;
                }
                else {
                    // world-diagonal facings (perpendicular to screen)
                    tiltPitch = 25;
                    tiltYaw = 225 - 90 * ((ramp - 1) % 4);
                }
                tilt *= Matrix4.CreateRotationX(MathHelper.DegreesToRadians(tiltPitch));
                tilt *= Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(tiltYaw));

                /*// show tilt direction
                GL.Color3(Color.Black);
                GL.Begin(BeginMode.Lines);
                GL.Vertex3(Vector3.Zero);
                var tiltVec = Vector3.UnitZ;
                tiltVec = Vector3.Transform(tiltVec, tilt);
                tiltVec = Vector3.Multiply(tiltVec, 1000f);
                GL.Vertex3(tiltVec);
                GL.End();*/
            }

            /*// draw slope normals
            GL.LineWidth(2);
            var colors = new[] { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange, Color.Black, Color.Purple, Color.SlateBlue, Color.DimGray, Color.White, Color.Teal, Color.Tan };
            for (int i = 0; i < 8; i++) {
                GL.Color3(colors[i]);

                const float roll = 25f;
                float syaw = 45f * i;
                var slopeNormal = Vector3.UnitZ;
                slopeNormal = Vector3.Transform(slopeNormal, Matrix4.CreateRotationX(MathHelper.DegreesToRadians(roll)));
                slopeNormal = Vector3.Transform(slopeNormal, Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(syaw)));
                GL.Begin(BeginMode.Lines);
                GL.Vertex3(0, 0, 0);
                GL.Vertex3(Vector3.Multiply(slopeNormal, 1000f));
                GL.End();
            }*/

            // object rotation around Z
            float direction = (obj is OwnableObject) ? (obj as OwnableObject).Direction : 0;
            float objectRotation = 90 - direction / 256f * 360f - tiltYaw; // convert game rotation to world degrees
            Matrix4 @object = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(objectRotation)) * tilt; // object facing
            // art.ini TurretOffset value positions some voxel parts over our x-axis
            @object = Matrix4.CreateTranslation(0.18f * props.TurretVoxelOffset, 0, 0) * @object;
            GL.MultMatrix(ref @object);

            // DrawAxes();

            float pitch = MathHelper.DegreesToRadians(210);
            float yaw = MathHelper.DegreesToRadians(120);
            /*// helps to find good pitch/yaw
            // direction of light vector given by pitch & yaw
            for (int i = 0; i < 360; i += 30) {
                for (int j = 0; j < 360; j += 30) {
                    GL.Color3(colors[i / 30]);
                    var shadowTransform2 =
                        Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(i))
                        * Matrix4.CreateRotationY(MathHelper.DegreesToRadians(j));
                    GL.LineWidth(2);
                    GL.Begin(BeginMode.Lines);
                    GL.Vertex3(0, 0, 0);
                    GL.Vertex3(Vector3.Multiply(ExtractRotationVector(ToOpenGL(Matrix4.Invert(world * shadowTransform2))), 100f));
                    GL.End();
                }
            }*/

            var shadowTransform = Matrix4.CreateRotationZ(pitch) * Matrix4.CreateRotationY(yaw);
            // clear shadowbuf
            var shadBuf = _surface.GetShadows();
            Array.Clear(shadBuf, 0, shadBuf.Length);

            foreach (var section in vxl.Sections) {
                GL.PushMatrix();

                var frameRot = hva.LoadGLMatrix(section.Index);
                frameRot.M41 *= section.HVAMultiplier * section.ScaleX;
                frameRot.M42 *= section.HVAMultiplier * section.ScaleY;
                frameRot.M43 *= section.HVAMultiplier * section.ScaleZ;

                var frameTransl = Matrix4.CreateTranslation(section.MinBounds);
                var frame = frameTransl * frameRot;
                GL.MultMatrix(ref frame);

                var shadowScale = Matrix4.Scale(0.5f);
                //var shadowTilt = null;
                var shadowToScreen = frameTransl * shadowScale * frameRot * (@object * world) * trans * lookat;

                // undo world transformations on light direction
                var v = @object*world*frame*shadowTransform;

                var lightDirection = (v.Determinant != 0.0) ? ExtractRotationVector(ToOpenGL(Matrix4.Invert(v))) : Vector3.Zero;

                // draw line in direction light comes from
                /*GL.Color3(Color.Red);
                GL.LineWidth(4f);
                GL.Begin(BeginMode.Lines);
                GL.Vertex3(0, 0, 0);
                GL.Vertex3(Vector3.Multiply(lightDirection, 100f));
                GL.End();*/

                GL.Begin(BeginMode.Quads);
                for (uint x = 0; x != section.SizeX; x++) {
                    for (uint y = 0; y != section.SizeY; y++) {
                        foreach (VxlFile.Voxel vx in section.Spans[x, y].Voxels) {
                            Color color = obj.Palette.Colors[vx.ColorIndex];
                            Vector3 normal = section.GetNormal(vx.NormalIndex);
                            // shader function taken from https://github.com/OpenRA/OpenRA/blob/bleed/cg/vxl.fx
                            // thanks to pchote for a LOT of help getting it right
                            Vector3 colorMult = Vector3.Add(Ambient, Diffuse * Math.Max(Vector3.Dot(normal, lightDirection), 0f));
                            GL.Color3(
                                (byte)Math.Min(255, color.R * colorMult.X),
                                (byte)Math.Min(255, color.G * colorMult.Y),
                                (byte)Math.Min(255, color.B * colorMult.Z));

                            Vector3 vxlPos = Vector3.Multiply(new Vector3(x, y, vx.Z), section.Scale);
                            RenderVoxel(vxlPos);

                            var shadpos = new Vector3(x, y, 0);
                            var screenPos = Vector3.Transform(shadpos, shadowToScreen);
                            screenPos = Vector3.Transform(screenPos, persp);
                            screenPos.X /= screenPos.Z;
                            screenPos.Y /= screenPos.Z;
                            screenPos.X = (screenPos.X + 1) * _surface.Width / 2;
                            screenPos.Y = (screenPos.Y + 1) * _surface.Height / 2;

                            if (0 <= screenPos.X && screenPos.X < _surface.Width && 0 <= screenPos.Y && screenPos.Y < _surface.Height)
                                shadBuf[(int)screenPos.X + (_surface.Height - 1 - (int)screenPos.Y) * _surface.Width] = true;

                            /* draw line in normal direction
                            if (r.Next(100) == 4) {
                                float m = Math.Max(Vector3.Dot(normal, lightDirection), 0f);
                                GL.Color3(m, m, m);
                                GL.LineWidth(1);
                                GL.Begin(BeginMode.Lines);
                                GL.Vertex3(new Vector3(x, y, vx.Z));
                                GL.Vertex3(new Vector3(x, y, vx.Z) + Vector3.Multiply(normal, 100f));
                                GL.End();
                            }*/

                        }
                    }
                }
                GL.End();
                GL.PopMatrix();
            }

            // read pixels back to surface
            GL.ReadPixels(0, 0, _surface.BitmapData.Width, _surface.BitmapData.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, _surface.BitmapData.Scan0);
            return _surface;
        }