A simple Example of using Video Textures in Managed DirectX (C#)

来源:互联网 发布:linux 服务器 ping 编辑:程序博客网 时间:2024/05/20 00:11

You may be confused how to use video textures in managed directX as some students of mine,"because Tom Miller didn't explain detail enough." Well, This article may solve your problem. hehe,Tom has done a great job for the 《Managed DirectX 9》,and the MDX SDK has also given the clue of such cases.

In this article I'll give the sample code fo using video textures on a cube,you can also control the video's play and pause step forward,backward and so on.

This article assumes you are familiar with C#,and the MDX9.0c SDK has been installed on your PC.

Before giving the code,let's see what Tom said :

"Another common feature for video files that users must have is the capability to play these movies onto a 3D texture inside the application they are building. Many of the developers use movie files as the "cut-scenes" during their game, and need some way to show the movie in full screen mode. The Audio Video playback classes can handle both cases quite well.

For the simplest case of a developer wanting to use a full screen movie as a cut-scene in the game they are writing, we made it as simple as could be. Simply load the video file into your Video object, set the "Fullscreen" property to true, and play the video. Nothing could be simpler…well, unless there was a constructor that took these parameters.

For the more difficult scenario where the developer has some set of geometry they want to texture using a video, we've come up with a different solution. First, there is an event you will want to hook, TextureReadyToRender. This event will be fired every time there is a new frame in the video file that is ready to be rendered as a texture.

The TextureRenderEventArgs member that is passed into the event handler contains the newly created texture that is ready to be rendered. This event may be fired from many different threads, so ensure that you do not have multiple threads accessing variables simultaneously. Who knows what craziness you can end up with doing that? All you need to do to start the video playing and the event firing is call the method RenderToTexture on the video object. You should begin receiving the events and textures immediately.

The sample that ships with the DirectX SDK renders the entire scene at the same frame rate as the movie being played; it only renders each frame when a new texture is ready. In many cases, this is adequate for the developers, since movies being rendered as a texture is a fairly uncommon occurrence. However, you may still want to use the texture in your "real time" rendering application, so how would you go about that?

First, you will need to detect whether your video texture has been created in the system memory pool or the default pool (since you won't have control over this). You can do this by checking the level description of the first level of the texture.

If the texture was created in the default pool (most common), you will need to use the Direct3D device's method GetRenderTargetData on the surface of the texture and a surface of your own created in the system memory pool. You cannot render textures created in the system memory pool, so you will also need to create a texture in the default memory pool to use for rendering. Something like

SurfaceDescription ds = e.Texture.GetLevelDescription(0);if (ds.Pool == Pool.Default){    systemSurface = device.CreateOffscreenPlainSurface(ds.Width, ds.Height,        ds.Format, Pool.SystemMemory);}texture = new Texture(device, ds.Width, ds.Height,    1, Usage.Dynamic, ds.Format, Pool.Default);

This code will check to see whether your video texture has been created in the default memory pool, and if it has, it will create an off-screen plain surface to hold the texture data, plus a texture in the default memory pool that we can use to render our scene. After this is executed, there are two methods on the device that we need to use to get the data from one texture to the next:

using(Surface videoSurface = e.Texture.GetSurfaceLevel(0)){    using(Surface textureSurface = texture.GetSurfaceLevel(0))    {        device.GetRenderTargetData(videoSurface, systemSurface);        device.UpdateSurface(systemSurface, textureSurface);    }}

As you can see here, we get the render target data from our video texture and move it into our temporary system memory surface. From there, we move it into our real texture. We can then use the texture we created in the default pool as the texture to render our scene.

Since the texture will be accessed by more than one thread, you will need to ensure that only one thread at a time has access to this texture by using the lock semantics on it.

The Audio and Video playback classes are not designed to be fully featured implementations of DirectShow. They do not offer many of the in-depth features, such as video capture or filter graph management. They are designed for the simplest cases of loading audio and video data, and playing this data back; nothing more. They are quick and easy to use, but should not be considered a full and robust solution, nor a "replacement" to the DirectShow APIs already out there.

There are no planned updates for this namespace or references."

If you understand well of  those words above,it'll be easy for you to understand my following code:

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.AudioVideoPlayback;

namespace VideoText_1
{
    
public partial class MainForm : Form
    
{        
        
private Video video;
        
private Device d3ddevice;
        
private VertexBuffer vertexbuff;
        
private IndexBuffer ib;//we don't use ib this time.
                     
        
private float ElapsedTime;
        
private DateTime CurrentTime;
        
private DateTime LastTime;

        
private float XR = 0.0f;
        
private float YR = 0.0f;
        
private float ZR = 0.0f;

        
double video_ave = 0;
        
double video_length = 0;

        
int k = 0;//parity check

        
struct Vertex
        
{
            
float x, y, z;
            
float tu, tv;

            
public Vertex(float X, float Y, float Z, float TU, float TV)
            
{
                x 
= X;
                y 
= Y;
                z 
= Z;

                tu 
= TU;
                tv 
= TV;
            }


            
public static readonly VertexFormats Flags = VertexFormats.Position | VertexFormats.Texture1;
        }


        
// the cube
        private Vertex[] Cube =
        
{
            
new Vertex(-10.0f10.0f,-10.0f,  0.0f,0.0f ),//0
            new Vertex( 10.0f10.0f,-10.0f,  0.25f,0.0f ),//1
            new Vertex(-10.0f,-10.0f,-10.0f,  0.0f,1.0f ),//2

            
new Vertex( 10.0f10.0f,-10.0f,  0.25f,0.0f ),//1
            new Vertex( 10.0f,-10.0f,-10.0f,  0.25f,1.0f ),//3
            new Vertex(-10.0f,-10.0f,-10.0f,  0.0f,1.0f ),//2
            
            
new Vertex( 10.0f,-10.0f10.0f,  0.5f,1.0f ),//7            
            new Vertex( 10.0f,-10.0f,-10.0f,  0.25f,1.0f ),//3
            new Vertex( 10.0f10.0f,-10.0f,  0.25f,0.0f ),//1

            
new Vertex( 10.0f10.0f,-10.0f,  0.25f,0.0f ),//1
            new Vertex( 10.0f10.0f10.0f,  0.5f,0.0f ),//6
            new Vertex( 10.0f,-10.0f10.0f,  0.5f,1.0f ),//7

            
new Vertex( 10.0f10.0f10.0f,  0.5f,0.0f ),//6
            new Vertex(-10.0f10.0f10.0f,  0.75f,0.0f ),//4
            new Vertex(-10.0f,-10.0f10.0f,  0.75f,1.0f ),//5

            
new Vertex(-10.0f,-10.0f10.0f,  0.75f,1.0f ),//5
            new Vertex( 10.0f,-10.0f10.0f,  0.5f,1.0f ),//7
            new Vertex( 10.0f10.0f10.0f,  0.5f,0.0f ),//6

            
new Vertex(-10.0f10.0f10.0f,  0.75f,0.0f ),//4
            new Vertex(-10.0f10.0f,-10.0f,  1.0f,0.0f ),//0
            new Vertex(-10.0f,-10.0f,-10.0f,  1.0f,1.0f ),//2

            
new Vertex(-10.0f,-10.0f,-10.0f,  1.0f,1.0f ),//2
            new Vertex(-10.0f,-10.0f10.0f,  0.75f,1.0f ),//5
            new Vertex(-10.0f10.0f10.0f,  0.75f,0.0f ),//4

            
//new Vertex(-10.0f, 10.0f, 10.0f,  0.0f,0.0f ),
            
//new Vertex( 10.0f, 10.0f, 10.0f,  1.0f,0.0f ),
            
//new Vertex(-10.0f, 10.0f,-10.0f,  0.0f,1.0f ),
            
//new Vertex( 10.0f, 10.0f,-10.0f,  1.0f,1.0f ),

            
//new Vertex(-10.0f,-10.0f, 10.0f,  0.0f,0.0f ),
            
//new Vertex(-10.0f,-10.0f,-10.0f,  1.0f,0.0f ),
            
//new Vertex( 10.0f,-10.0f, 10.0f,  0.0f,1.0f ),
            
//new Vertex( 10.0f,-10.0f,-10.0f,  1.0f,1.0f ),

            
//new Vertex( 10.0f, 10.0f,-10.0f,  0.0f,0.0f ),
            
//new Vertex( 10.0f, 10.0f, 10.0f,  1.0f,0.0f ),
            
//new Vertex( 10.0f,-10.0f,-10.0f,  0.0f,1.0f ),
            
//new Vertex( 10.0f,-10.0f, 10.0f,  1.0f,1.0f ),

            
//new Vertex(-10.0f, 10.0f,-10.0f,  1.0f,0.0f ),
            
//new Vertex(-10.0f,-10.0f,-10.0f,  1.0f,1.0f ),
            
//new Vertex(-10.0f, 10.0f, 10.0f,  0.0f,0.0f ),
            
//new Vertex(-10.0f,-10.0f, 10.0f,  0.0f,1.0f ),
            
                        
        }
;
        
//private static readonly short[] indices =
        
//{
        
//        0,1,2, // Front Face 
        
//        1,3,2, // Front Face 
        
//        1,6,3, // Back Face 
        
//        6,7,3, // Back Face 
        
//        6,4,5, // Top Face 
        
//        5,7,6, // Top Face 
        
//        4,0,5, // Bottom Face 
        
//        0,2,5, // Bottom Face 
        
//        //0,6,1, // Left Face 
        
//        //4,6,0, // Left Face 
        
//        //2,3,7, // Right Face 
        
//        //5,2,7 // Ri
        
//};

        
/// <summary>
        
/// Nasky
        
/// </summary>

        public MainForm()
        
{
            InitializeComponent();
        }


        
/// <summary>
        
/// Direct3D Initialization
        
/// </summary>

        private void InitD3D()
        
{
            
          
           
 PresentParameters d3dpp = new PresentParameters();
            d3dpp.BackBufferFormat 
= Format.Unknown;
            d3dpp.SwapEffect 
= SwapEffect.Discard;
            d3dpp.Windowed 
= true;
            d3dpp.EnableAutoDepthStencil 
= true;
            d3dpp.AutoDepthStencilFormat 
= DepthFormat.D24X8;
            d3dpp.PresentationInterval 
= PresentInterval.Immediate;

            

            d3ddevice = new Device(0, DeviceType.Hardware, this.Handle, CreateFlags.SoftwareVertexProcessing, d3dpp);
            d3ddevice.DeviceReset 
+= new System.EventHandler(OnResetDevice);

            OnResetDevice(d3ddevice, 
null);
            
//ib = new IndexBuffer(typeof(short), indices.Length, d3ddevice, Usage.WriteOnly, Pool.Default);
            
//ib.Created += new EventHandler(OnIbCreated);
            
//OnIbCreated(ib, null);
        }


        
/// <summary>
        
/// Nasky
        
/// </summary>
        
/// 

        //private void OnIbCreated(object sender, EventArgs e)
        
//{
        
//    IndexBuffer IbTemp = (IndexBuffer)sender;
        
//    IbTemp.SetData(indices, 0, LockFlags.None);
        
//}
        public void OnResetDevice(object sender, EventArgs e)
        
{
            Device device 
= (Device)sender;

            device.Transform.Projection 
=
                Matrix.PerspectiveFovLH(Geometry.DegreeToRadian(
45.0f),
                (
float)ClientSize.Width / ClientSize.Height,
                
0.1f100.0f);

            device.RenderState.ZBufferEnable 
= true;
            device.RenderState.Lighting 
= false;
            device.RenderState.CullMode 
= Cull.None;

            
    vertexbuff = new VertexBuffer(
                
typeof(Vertex), Cube.Length, device,
                Usage.Dynamic 
| Usage.WriteOnly,
                Vertex.Flags, Pool.Default
            );

            GraphicsStream gStream 
= vertexbuff.Lock(00, LockFlags.None);
            gStream.Write(Cube);
            vertexbuff.Unlock();
        }


        
/// <summary>
        
/// Nasky
        
/// </summary>

        private void MainForm_Shown(object sender, EventArgs e)
        
{
            
if (odVideoFiles.ShowDialog() == DialogResult.OK)
            
{
                

                InitD3D();

                // (DirectShow VMR-9)

                video = new Video(odVideoFiles.FileName);
                video_length 
= video.Duration;//get the whole time_length of the file
                video_ave = video.AverageTimePerFrame;//get the average_time_per_frame of the file;
                video.Disposing += delegate while (true); };
                video.TextureReadyToRender 
+= new TextureRenderEventHandler(video_TextureReadyToRender);
                video.RenderToTexture(d3ddevice);
                video.Play();
                video.Pause();
                
            }

        }


        
/// <summary>
        
/// Nasky...
        
/// </summary>

        private void video_TextureReadyToRender(object sender, TextureRenderEventArgs e)
        
{
            
            d3ddevice.Clear(ClearFlags.Target 
| ClearFlags.ZBuffer, Color.BlueViolet, 1.0f0);
            
            CurrentTime 
= DateTime.Now;
            TimeSpan ElapsedTimeSpan 
= CurrentTime.Subtract(LastTime);
            ElapsedTime 
= (float)ElapsedTimeSpan.Milliseconds * 0.001f;
            LastTime 
= CurrentTime;

            XR 
+= 50.0f * ElapsedTime;
            YR 
+= 60.0f * ElapsedTime;
            ZR 
+= 70.0f * ElapsedTime;

            d3ddevice.BeginScene();

            
            d3ddevice.Transform.World 
=
                    Matrix.RotationYawPitchRoll(Geometry.DegreeToRadian(XR),
                                                Geometry.DegreeToRadian(YR),
                                                Geometry.DegreeToRadian(ZR)) 
* Matrix.Translation(0.0f0.0f5.0f);

            Matrix.Translation(
0.0f0.0f5.0f);
            
            d3ddevice.Transform.View 
= Matrix.LookAtLH(new Vector3(0050), new Vector3(000), new Vector3(010));

            d3ddevice.SetTexture(
0, e.Texture);

            d3ddevice.VertexFormat 
= Vertex.Flags;
            d3ddevice.SetStreamSource(
0, vertexbuff, 0);

            
            d3ddevice.DrawPrimitives(PrimitiveType.TriangleList, 
08);
            
            

            d3ddevice.EndScene();

            
            d3ddevice.Present();                  
        }

        
protected override void OnKeyDown(KeyEventArgs e)
        
{
            
if ((e.KeyValue == 68&& (video.CurrentPosition < video_length))
            
{

                
double getposition = video.CurrentPosition;
                video.CurrentPosition 
= getposition + video_ave;

            }

            
if ((e.KeyValue == 65&& (video.CurrentPosition > 0))
            
{

                
double getposition = video.CurrentPosition;
                video.CurrentPosition 
= getposition - video_ave;

            }

            
            
if ((e.KeyValue) == 27)//Esc
            {
                
this.Close();
                Application.Exit();
            }

            
if (e.KeyValue == 13)//Enter
            {
                k
++;
                
if (k % 2 != 0)
                
{
                    video.Play();
                }

                
else
                
{
                    video.Pause();
                }

            }

        }

    }

}

 

 

and here is the running result:

                                                                            R-1

                                                                           R-2

                                                                          R-3

                                                                         R-4

Well,you may select every video file you like to have a try.Hope this can help you.

Nasky