Implementation Guide

This section steps you through building an image or mesh transform dynamic-link library (DLL) using Microsoft® Visual C++® and the Microsoft® DirectX® Transform interfaces.

  1. Create a new Microsoft Active Template Library (ATL) project from Visual C++ by opening the File menu and choosing the New command. Select the Projects tab, and then "ATL COM AppWizard".
  2. Enter the new project name and choose OK.
  3. Set the server type by selecting the Dynamic Link Library option button and choose Finish.
  4. Open Stdafx.h and insert the following immediately after the include statement for Atlcom.h.
    #include <Dxtrans.h>
    #include <Dtbase.h>
    #include <Dxatlpb.h>
    
  5. Open Stdafx.cpp and insert the following include statements as the last lines of the file immediately after the include of Atlimpl.cpp.
    #include <Dtbase.cpp>
    #include <Dxtguid.c>
    #include <Atlctl.cpp>
    
  6. Open the project's .idl file and add the following line after the import of Ocidl.idl.
    import "Dxtrans.idl";
    
  7. Build the project. If the compiler cannot find one or more of the files, you have not yet added the DirectX Media directory to your path. You can do one of the following:
    • To change your global settings to always use the DirectX Media directory, open the Tools menu and choose the Options command. Select the Directories tab, and add the DXMedia\Include and DXMedia\Lib directories to the corresponding categories, respectively. Make sure each is the first entry in its list.
    • To set the DirectX Media directory for only this project, select Project, then Settings from the main menu bar. In the Settings For drop-down list box, select "All Configurations". Select the C/C++ tab, select "Category of Preprocessor", add the directory to the Additional Include Directories control, and choose OK.
  8. Add a new ATL object to the project after you have confirmed that the project builds and links.
    • On the Insert menu, choose New ATL Object.
    • In the dialog box, select Simple Object and choose Next.
    • From the Names tab, enter a name for the object in the Short Name text field (named "MyEffect" in the following example).
    • Select the Attributes tab.
    • Under Threading Model, select Both.
    • Under Interface, select Dual.
    • Under Aggregation, select Yes.
    • Select the Free Threaded Marshaler check box.
    • Choose OK.
  9. Find the definition of your class's interface in the .idl file and change the inheritance from IDispatch to IDXEffect.
  10. If your transform will use custom properties, you will need to add a property page to the project.
    • Select Insert, and then New ATL Object.
    • On the dialog box that appears, select Controls in the left window, select Property Page in the right window, and choose Next.
    • From the Names tab, enter a name for the object in the Short Name text field (named "MyEffectPP" in the following example).
    • Select the Attributes tab.
    • Under Threading Model, select Both.
    • Under Interface, select Dual.
    • Under Aggregation, select Yes.
    • Select the Free Threaded Marshaler check box.
    • Choose OK.
  11. Open the "MyEffect.h" file and make the following changes.
    • Make your class inherit from the base class by replacing the following:
      public CComObjectRootEx<CComMultiThreadModel>,
      

      with the line:

      public CDXBaseNTo1,
    • Inherit from the following additional items.
      public CComPropertySupport<CMyEffect>,
      public IObjectSafetyImpl2<CMyEffect>,
      public IPersistStorageImpl<CMyEffect>,
      public ISpecifyPropertyPagesImpl<CMyEffect>,
      public IPersistPropertyBagImpl<CMyEffect>
      
    • Add the following lines of code immediately before END_COM_MAP in the COM_MAP.
      COM_INTERFACE_ENTRY(IDXEffect)
      COM_INTERFACE_ENTRY_IID(IID_IObjectSafety,
      IObjectSafetyImpl2<CMyEffect>)
      COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
      COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
      COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
      COM_INTERFACE_ENTRY_CHAIN(CDXBaseNTo1)
      
    • Change the registration entry from:
      DECLARE_REGISTRY_RESOURCEID(IDR_MyEffect)

      to:

      DECLARE_REGISTER_DX_TRANSFORM(IDR_MyEffect,
      CATID_propercategory)

      For a three-dimensional (3-D) transform, the category ID would be CATID_DX3DTransform. For a two-dimensional (2-D) transform, the category ID would be CATID_DXImageTransform.

    • Declare the object as aggregatable by adding the following line.
      DECLARE_POLY_AGGREGATABLE(CMyEffect)
    • Add the following in the class declaration.
      DECLARE_IDXEFFECT_METHODS(Caps)

      Caps specifies the value you want to have your transform return from the IDXEffect::get_Capabilities method. This can be a combination of the flags in the DXEFFECTTYPE enumeration, or zero. In the case of the sample, it is not a morph (DXTET_MORPH, two input transforms), and it is not periodic (DXTET_PERIODIC, where the result at one is not the same as the result at zero), so the Caps value is zero.

    • Add a property map for the object by inserting the following within the class definition body.
      BEGIN_PROPERTY_MAP( CMyEffect )
          PROP_ENTRY("Description", DISPID_MyProperty,
          CLSID_MyEffectPP )
          PROP_PAGE( CLSID_MyEffectPP )
      END_PROPERTY_MAP()
      

      There must be a PROP_ENTRY for each custom property you want your transform to support. You can fill these items in later, when you have defined these properties for your transform. The PropPageClsid is located at the top of your MyEffectPP.h header file. If your transform has no custom properties, the property map should be omitted.

  12. Add the core of the program. There are three main steps to add: Creation, Setup, and Execution.

    Creation

    In the body of the constructor for your object, you will need to set data members in the base class to configure your transform. The following table describes these data members.
    m_ulMaxInputs Maximum number of input objects allowed. Default value is 1.
    m_ulNumInRequired Number of input objects required. Default value is 1.
    m_dwOptionFlags Used to indicate the type of inputs and outputs for the transform. The default option assumes that DXSurface objects are used for both input and output. If your transform is using 3-D meshes for input, you should set the DXBOF_INPUTS_MESHBUILDER flag for this member. If your transform is using 3-D meshes for output, you should set the DXBOF_OUTPUT_MESHBUILDER flag for this member.
    m_dwMiscFlags A combination of flags that will tell users of any special features supported by your transform. These are listed in the DXTMISCFLAGS enumeration. Supported options include:
    • DXTMF_BLEND_SUPPORTED
    • DXTMF_DITHER_SUPPORTED
    • DXTMF_INPLACE_OPERATION
    • DXTMF_BOUNDS_SUPPORTED
    • DXTMF_PLACEMENT_SUPPORTED
    • DXTMF_QUALITY_SUPPORTED

    If your transform supports none of these options, it should be set to 0.

    For example, a 3-D transform constructor might look like this:

    CMyEffect()
    {
        m_dwOptionFlags =
        DXBOF_INPUTS_MESHBUILDER | DXBOF_OUTPUTS_MESHBUILDER
        m_dwMiscFlags = 0;
    }
    

    Setup

    Set up the transform by overriding the virtual method CDXBaseNTo1::OnSetup in the file MyEffect.cpp. The sample transform's OnSetup code looks like the following:

    HRESULT CMyEffect::OnSetup(DWORD dwFlags)
    {
        return OutputMeshBuilder()->AddMeshBuilder(
        InputMeshBuilder(),0);
    }
    

    In the previous example code, note that when OnSetup is called, the transform can access the OutputMeshBuilder and InputMeshBuilder member functions. OnSetup will only be called for a nonempty call to the IDXTransform::Setup method after all objects have been validated. In the case of a 3-D transform, the base class checks that the appropriate number of inputs are provided and that the input and output are mesh builder objects. During the OnSetup call, the transform should perform any one-time setup that does not need to happen every time OnExecute is called.

    Execution

    To implement the work procedure of a 3-D transform, you override the base class OnExecute method. Image transforms override the base class WorkProc method. When these methods are called, the base class has already ensured that the caller has not passed any unsupported parameters and also checked that the transform has been properly set up. See the sample code for the Wipe example's WorkProc method. The reason there are two methods is because images are automatically broken up into bands by the base class and executed in parallel on the available processors.

  13. Add support code for the transform property page.
    • For each private data member of your transform that represents a custom property, you need to implement put_ and get_ access functions. These are often declared in your MyEffect.h header file in the following manner.
          STDMETHOD( get_MyProperty )( float *pVal );
          STDMETHOD( put_MyProperty )( float newVal );
      
    • Add dispatch interface support for the MyEffect interface. Create an enumeration for the dispatcher identifiers, with each property an element of the enumeration. Place it just before the entry for MyEffect in the project's .idl file.
      typedef enum MyEffectDISPID
      {
          DISPID_MyEffect_MyProperty1 = DISPID_DXE_NEXT_ID,
          DISPID_MyEffect_MyProperty2,
      } MyEffectDISPID;
      

      Add dispatcher methods to the MyEffect interface, as shown in the following code.

      interface MyEffect : IDXEffect
      {
          [propget, id(DISPID_MyEffect_MyProperty1), helpstring(
          "property MyProperty1")] HRESULT MyProperty1
          ([out, retval] float *pVal);
          [propput, id(DISPID_MyEffect_MyProperty1), helpstring(
          "property MyProperty1")] HRESULT MyProperty1
          ([in] float newVal);
          [propget, id(DISPID_MyEffect_MyProperty2), helpstring(
          "property MyProperty2")] HRESULT MyProperty2
          ([out, retval] float *pVal);
          [propput, id(DISPID_MyEffect_MyProperty2), helpstring(
          "property MyProperty2")] HRESULT MyProperty2
          ([in] float newVal);
      };
      

      You should then fill out the PROPERTY_MAP in the MyEffect.h file.

    • Inside the resource file for the project, there should be a dialog box entry for your property page. You need to edit this to reflect the custom properties that people can change on your transform.
    • In the MyEffectPP.cpp file, override the OnInitDialog method. It should use the get_MyProperty method for each property to set the initial values of the dialog box.
    • In the MyEffectPP.cpp file, override the Apply method. It should read each of the values from the dialog box and use the put_MyProperty method to set the values in the transform.
  14. Implement surface picking. For image transforms with more than one input, you need to override the CDXBaseNTo1::OnGetSurfacePickOrder function with a function that chooses one of the input surfaces based on a selected point on the output surface. For a custom surface picking implementation, you will need to override the CDXBaseNTo1::OnSurfacePick method.

    If your output is a 3-D mesh, you need to use tools in Direct3D Retained Mode to implement mesh picking. For more information, see Author's Guide to Transforms.

You can use the DXETool.exe application to test and debug your transform. It is included with the SDK in the DXMedia\bin\ directory. For more information, see DXETool for Writers.


Top of Page Top of Page
© 2000 Microsoft and/or its suppliers. All rights reserved. Terms of Use.