Topic : Mip-Mapping in Direct3D
Author : Johnathan Skinner
Page : << Previous 2  
Go to page :


= 1;          /* number of mip-map levels to create
                                     (1 = just original texture) */
    DWORD flags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
    DWORD caps = DDSCAPS_TEXTURE | DDSCAPS_3DDEVICE;
    HRESULT result;

    /* put texture in video memory if driver allows it */
    if (VideoMemoryTexturesAllowed())
      caps |= DDSCAPS_VIDEOMEMORY;
    else
      caps |= DDSCAPS_SYSTEMMEMORY;

    if (mipmap)
    {
      /* count how many mip-map levels we need */
      int mipWidth = width;
      int mipHeight = height;

      /* smallest mip-map we want is 2 x 2 */
      while ((mipWidth > 2) && (mipHeight > 2))
      {
        mipLevels++;
        mipWidth  /= 2;
        mipHeight /= 2;
      }

      if (mipLevels > 1)
      {
        /* tell it we want mip-maps */
        flags |= DDSD_MIPMAPCOUNT;
        caps  |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
      }
    }

    /* set up buffer properties */
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize          = sizeof(ddsd);
    ddsd.dwFlags         = flags;
    ddsd.dwWidth         = width;
    ddsd.dwHeight        = height;
    ddsd.ddpfPixelFormat = *pixelFormat;
    ddsd.ddsCaps.dwCaps  = caps;
    ddsd.dwMipMapCount   = mipLevels;

    /* create texture surface and associated mip-maps */
    result = IDirectDraw_CreateSurface( lpDD, &ddsd, &surface, NULL);

    if (result != DD_OK) return NULL;

    return surface;
  }



The CreateSurface() call here will create the number of surfaces you specify in ddsd.dwMipMapCount, but it only returns a pointer to one surface. The mip-map surfaces are accessed by calling GetAttachedSurface(). There is no need to do anything special in clean-up because all auto-generated surfaces are released with the parent surface.

Now that the mip-maps have been created, you will need to put in the texture images. Using the images generated from the code sample in the 'Mip-Map Factory' section of this article, you can copy the images to the mip-maps like so:


  Texture *textureData;
  LPDIRECTDRAWSURFACE surface, mipSurface, nextSurface;
  DDSURFACEDESC ddsd;

  /* generate the mip-maps from the original image */
  MakeMipMaps( originalImage);

  /* create the texture surfaces */
  surface = CreateD3DTexture( lpDD, originalImage->width,
                              originalImage->height, TRUE, pixelFormat);

  /* iterate through the mip-maps and copy the data */
  textureData = originalImage;
  mipSurface = surface;

  while (textureData != NULL)
  {
    /* lock surface so we can write to it */
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    IDirectDraw_Lock( mipSurface, NULL, &ddsd, 0, NULL);

    /* copy data from from image to the texture surface */
    CopyTextureData(
      ddsd.lpSurface, /* pointer to destination surface data */
      ddsd.lPitch,    /* number of bytes per horizontal span in the dest surface */
      textureData);   /* pointer to source image */

    /* done writing surface so unlock the memory */
    IDirectDraw_Unlock( mipSurface, NULL);

    /* follow the links down to through the mip-map levels of texture data */
    textureData = textureData->mipMap;
    if (textureData == NULL)
    {
      /* done the last mip-map, need to release the reference to the surface */
      /* don't want to release the first surface or it will free the whole thing! */
      if (mipSirface != surface)
        IDirectDraw_Release( mipSurface);
      break;
    }

    /* get the surface for the next level mip-map */
    IDirectDraw_GetAttachedSurface( mipSurface, &ddsd.ddsCaps, &nextSurface);

    /* need to release the reference to the parent surface */
    if (mipSirface != surface)
      IDirectDraw_Release( mipSurface);

    mipSurface = nextSurface;
  }

  /* now the textures are loaded, we can grab the handle and start referencing it,
     just like with a regular texture */
    . . .
  IDirectDraw_QueryInterface(surface, IID_IDirect3DTexture, (void**)&D3DTexture);
  IDirect3DTexture_GetHandle( D3DTexture, lpD3DDevice, &handle);
    . . .



Now that the image data for the textures has been loaded, it's pretty much ready to go. The only thing missing is to tell Direct3D that you want it to render the mip-maps. If you were to render it now just like any other texture, it will only use the original full-sized texture. In order to get it to use the mip-maps, we must set the render state D3DRENDERSTATE_TEXTUREMIN to one of the following values:

D3DFILTER_MIPNEAREST - Use mip-maps with point sampling
D3DFILTER_MIPLINEAR - Use mip-maps with bilinear filtering (texture interpolation)
D3DFILTER_LINEARMIPNEAREST - Use mip-maps with point sampling, and apply a linear filter between each mip-map level and the next (smooths transitions between the different levels)
D3DFILTER_LINEARMIPLINEAR - Use mip-maps with bilinear filtering, and apply a linear filter between each mip-map level and the next
Throw that into your Direct3D code, and just like that, your textures are mip-mapped. Remember though, that just like with the rest of Direct3D, you will have to check the capabilities of the driver to make sure it supports these render states before you can use them.

If for some reason you want to render without the mip-maps, you can always set it back to either D3DFILTER_NEAREST or D3DFILTER_LINEAR.


In Closing


Hopefully by now you should have been able to seemlessly integrate mip-mapping into your Direct3D graphics engine, so that some day you can display it to thousands of on-lookers who will say "ooohh, aaahh, smooth..." and stare in wonderment at the absence of moiré patterns :)

Johnathan Skinner
jskinner@beyondvirtual.com

Page : << Previous 2