text:
"""
Interface functions to Mediawiki's api.php
"""
#
# (C) Pywikipedia bot team, 2007-12
#
# Distributed under the terms of the MIT license.
#
__version__ = '$Id$'

from UserDict import DictMixin
from pywikibot.comms import http
from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart
import datetime
import hashlib
import json
import logging
import mimetypes
import os
try:
    import cPickle as pickle
except ImportError:
    import pickle
import pprint
import re
import traceback
import time
import urllib
import warnings

import pywikibot
from pywikibot import config, login
from pywikibot.exceptions import *

_logger = "data.api"

lagpattern = re.compile(r"Waiting for [\d.]+: (?P<lag>\d+) seconds? lagged")

class APIError(pywikibot.Error):
    """The wiki site returned an error message."""
    def __init__(self, code, info, **kwargs):
        """Save error dict returned by MW API."""
        self.code = code
        self.info = info
        self.other = kwargs
        self.unicode = unicode(self.__str__())

    def __repr__(self):
        return 'APIError("%(code)s", "%(info)s", %(other)s)' % self.__dict__

    def __str__(self):
        return "%(code)s: %(info)s" % self.__dict__

class APIWarning(UserWarning):
    """The API returned a warning message."""
    pass

class TimeoutError(pywikibot.Error):
    pass

class Request(object, DictMixin):
    """A request to a Site's api.php interface.

    Attributes of this object (except for the special parameters listed
    below) get passed as commands to api.php, and can be get or set using
    the dict interface.  All attributes must be strings (or unicode).  Use
    an empty string for parameters that don't require a value. For example,
    Request(action="query", titles="Foo bar", prop="info", redirects="")
    corresponds to the API request
    "api.php?action=query&titles=Foo%20bar&prop=info&redirects"

    This is the lowest-level interface to the API, and can be used for any
    request that a particular site's API supports. See the API documentation
    (http://www.mediawiki.org/wiki/API) and site-specific settings for
    details on what parameters are accepted for each request type.

    Uploading files is a special case: to upload, the parameter "mime" must
    be true, and the parameter "file" must be set equal to a valid
    filename on the local computer, _not_ to the content of the file.

    Returns a dict containing the JSON data returned by the wiki. Normally,
    one of the dict keys will be equal to the value of the 'action'
    parameter.  Errors are caught and raise an APIError exception.

    Example:

    >>> r = Request(site=mysite, action="query", meta="userinfo")
    >>> # This is equivalent to
    >>> # http://{path}/api.php?action=query&meta=userinfo&format=json
    >>> # change a parameter
    >>> r['meta'] = "userinfo|siteinfo"
    >>> # add a new parameter
    >>> r['siprop'] = "namespaces"
    >>> # note that "uiprop" param gets added automatically
    >>> r.params
    {'action': 'query', 'meta': 'userinfo|siteinfo', 'siprop': 'namespaces'}
    >>> data = r.submit()
    >>> type(data)
    <type 'dict'>
    >>> data.keys()
    [u'query']
    >>> data[u'query'].keys()
    [u'userinfo', u'namespaces']

    @param site: The Site to which the request will be submitted. If not
           supplied, uses the user's configured default Site.
    @param mime: If true, send in "multipart/form-data" format (default False)
    @param max_retries: (optional) Maximum number of times to retry after
           errors, defaults to 25
    @param retry_wait: (optional) Minimum time to wait after an error,
           defaults to 5 seconds (doubles each retry until max of 120 is
           reached)
    @param format: (optional) Defaults to "json"

    """
    def __init__(self, **kwargs):
        try:
            self.site = kwargs.pop("site")
        except KeyError:
            self.site = pywikibot.Site()
        self.mime = kwargs.pop("mime", False)
        self.max_retries = kwargs.pop("max_retries", pywikibot.config.max_retries)
        self.retry_wait = kwargs.pop("retry_wait", pywikibot.config.retry_wait)
        self.params = {}
        if "action" not in kwargs:
            raise ValueError("'action' specification missing from Request.")
        self.update(**kwargs)
        self.write = self.params["action"] in (
                        "edit", "move", "rollback", "delete", "undelete",
                        "protect", "block", "unblock", "watch", "patrol",
                        "import", "userrights", "upload", "wbeditentity",
                        "wbsetlabel", "wbsetdescription", "wbsetaliases",
                        "wblinktitles", "wbsetsitelink", "wbcreateclaim",
                        "wbremoveclaims", "wbsetclaimvalue", "wbsetreference",
                        "wbremovereferences"
                    )
        if self.params["action"] == "edit":
            pywikibot.debug(u"Adding user assertion", _logger)
            self.params["assert"] = "user"  # make sure user is logged in

    # implement dict interface
    def __getitem__(self, key):
        return self.params[key]

    def __setitem__(self, key, value):
        self.params[key] = value

    def __delitem__(self, key):
        del self.params[key]

    def keys(self):
        return self.params.keys()

    def __contains__(self, key):
        return self.params.__contains__(key)

    def __iter__(self):
        return self.params.__iter__()

    def iteritems(self):
        return self.params.iteritems()

    def http_params(self):
        """Return the parameters formatted for inclusion in an HTTP request."""

        for key in self.params:
            if isinstance(self.params[key], basestring):
                # convert a stringified sequence into a list
                self.params[key] = self.params[key].split("|")
            try:
                iter(self.params[key])
            except TypeError:
                # convert any non-iterable value into a single-element list
                self.params[key] = [str(self.params[key])]
        if self.params["action"] == ['query']:
            meta = self.params.get("meta", [])
            if "userinfo" not in meta:
                meta.append("userinfo")
                self.params["meta"] = meta
            uiprop = self.params.get("uiprop", [])
            uiprop = set(uiprop + ["blockinfo", "hasmsg"])
            self.params["uiprop"] = list(uiprop)
            if "properties" in self.params:
                if "info" in self.params["properties"]:
                    inprop = self.params.get("inprop", [])
                    info = set(inprop + ["protection", "talkid", "subjectid"])
                    self.params["info"] = list(info)
        if "maxlag" not in self.params and config.maxlag:
            self.params["maxlag"] = [str(config.maxlag)]
        if "format" not in self.params:
            self.params["format"] = ["json"]
        if self.params['format'] != ["json"]:
            raise TypeError("Query format '%s' cannot be parsed."
                            % self.params['format'])
        for key in self.params:
            try:
                self.params[key] = "|".join(self.params[key])
                if isinstance(self.params[key], unicode):
                    self.params[key] = self.params[key].encode(
                                                self.site.encoding())
            except Exception:
                pywikibot.error(
u"http_params: Key '%s' could not be encoded to '%s'; params=%r"
                      % (key, self.site.encoding(), self.params[key]))
        return urllib.urlencode(self.params)

    def __str__(self):
        return urllib.unquote(self.site.scriptpath()
                              + "/api.php?"
                              + self.http_params()
                             )

    def _simulate(self, action):
        if action and config.simulate and action in config.actions_to_block:
            pywikibot.output(
                u'\03{lightyellow}SIMULATION: %s action blocked.\03{default}'
                % action)
            return {action: {'result': 'Success', 'nochange': ''}}

    def submit(self):
        """Submit a query and parse the response.

        @return:  The data retrieved from api.php (a dict)

        """
        paramstring = self.http_params()
        while True:
            action = self.params.get("action", "")
            simulate = self._simulate(action)
            if simulate:
                return simulate
            self.site.throttle(write=self.write)
            uri = self.site.scriptpath() + "/api.php"
            ssl = False
            if self.site.family.name in config.available_ssl_project:
                if action == "login" and config.use_SSL_onlogin:
                    ssl = True
                elif config.use_SSL_always:
                    ssl = True
            try:
                if self.mime:
                    # construct a MIME message containing all API key/values
                    container = MIMEMultipart(_subtype='form-data')
                    for key in self.params:
                        # key "file" requires special treatment in a multipart
                        # message
                        if key == "file":
                            local_filename = self.params[key]
                            filetype = mimetypes.guess_type(local_filename)[0] \
                                       or 'application/octet-stream'
                            file_content = file(local_filename, "rb").read()
                            submsg = MIMENonMultipart(*filetype.split("/"))
                            submsg.add_header("Content-disposition",
                                              "form-data", name=key,
                                              filename=local_filename)
                            submsg.set_payload(file_content)
                        else:
                            try:
                                self.params[key].encode("ascii")
                                keytype = ("text", "plain")
                            except UnicodeError:
                                keytype = ("application", "octet-stream")
                            submsg = MIMENonMultipart(*keytype)
                            submsg.add_header("Content-disposition", "form-data",
                                              name=key)
                            submsg.set_payload(self.params[key])
                        container.attach(submsg)
                    # strip the headers to get the HTTP message body
                    body = container.as_string()
                    marker = "\n\n" # separates headers from body
                    eoh = body.find(marker)
                    body = body[ eoh + len(marker): ]
                    # retrieve the headers from the MIME object
                    mimehead = dict(container.items())
                    rawdata = http.request(self.site, uri, ssl, method="POST",
                                           headers=mimehead, body=body)
                else:
                    rawdata = http.request(self.site, uri, ssl, method="POST",
                                headers={'Content-Type':
                                         'application/x-www-form-urlencoded'},
                                body=paramstring)
##                import traceback
##                traceback.print_stack()
##                print rawdata
            except Server504Error:
                pywikibot.log(u"Caught HTTP 504 error; retrying")
                self.wait()
                continue
            #TODO: what other exceptions can occur here?
            except Exception, e:
                # for any other error on the http request, wait and retry
                pywikibot.error(traceback.format_exc())
                pywikibot.log(u"%s, %s" % (uri, paramstring))
                self.wait()
                continue
            if not isinstance(rawdata, unicode):
                rawdata = rawdata.decode(self.site.encoding())
            pywikibot.debug(u"API response received:\n" + rawdata, _logger)
            if rawdata.startswith(u"unknown_action"):
                raise APIError(rawdata[:14], rawdata[16:])
            try:
                result = json.loads(rawdata)
            except ValueError:
                # if the result isn't valid JSON, there must be a server
                # problem.  Wait a few seconds and try again
                pywikibot.warning(
"Non-JSON response received from server %s; the server may be down."
                                 % self.site)
                pywikibot.debug(rawdata, _logger)
                # there might also be an overflow, so try a smaller limit
                for param in self.params:
                    if param.endswith("limit"):
                        value = self.params[param]
                        try:
                            self.params[param] = str(int(value) // 2)
                            pywikibot.output(u"Set %s = %s"
                                             % (param, self.params[param]))
                        except:
                            pass
                self.wait()
                continue
            if not result:
                result = {}
            if type(result) is not dict:
                raise APIError("Unknown",
                               "Unable to process query response of type %s."
                                   % type(result),
                               {'data': result})
            if self['action'] == 'query':
                if 'userinfo' in result.get('query', ()):
                    if hasattr(self.site, '_userinfo'):
                        self.site._userinfo.update(result['query']['userinfo'])
                    else:
                        self.site._userinfo = result['query']['userinfo']
                status = self.site._loginstatus  # save previous login status
                if ( ("error" in result
                            and result["error"]["code"].endswith("limit"))
                      or (status >= 0
                            and self.site._userinfo['name']
                                != self.site._username[status])):
                    # user is no longer logged in (session expired?)
                    # reset userinfo, then make user log in again
                    del self.site._userinfo
                    self.site._loginstatus = -1
                    if status < 0:
                        status = 0  # default to non-sysop login
                    self.site.login(status)
                    # retry the previous query
                    continue
            if "warnings" in result:
                modules = [k for k in result["warnings"] if k != "info"]
                for mod in modules:
                    pywikibot.warning(
                        u"API warning (%s): %s"
                         % (mod, result["warnings"][mod]["*"]))
            if "error" not in result:
                return result
            if "*" in result["error"]:
                # help text returned
                result['error']['help'] = result['error'].pop("*")
            code = result["error"].pop("code", "Unknown")
            info = result["error"].pop("info", None)
            if code == "maxlag":
                lag = lagpattern.search(info)
                if lag:
                    pywikibot.log(
                        u"Pausing due to database lag: " + info)
                    self.site.throttle.lag(int(lag.group("lag")))
                    continue
            if code in (u'internal_api_error_DBConnectionError', ):
                self.wait()
                continue
            # raise error
            try:
                pywikibot.log(u"API Error: query=\n%s"
                               % pprint.pformat(self.params))
                pywikibot.log(u"           response=\n%s"
                               % result)
                raise APIError(code, info, **result["error"])
            except TypeError:
                raise RuntimeError(result)

    def wait(self):
        """Determine how long to wait after a failed request."""
        self.max_retries -= 1
        if self.max_retries < 0:
            raise TimeoutError("Maximum retries attempted without success.")
        pywikibot.warning(u"Waiting %s seconds before retrying."
                          % self.retry_wait)
        time.sleep(self.retry_wait)
        # double the next wait, but do not exceed 120 seconds
        self.retry_wait = min(120, self.retry_wait * 2)

class CachedRequest(Request):
    def __init__(self, expiry, *args, **kwargs):
        """ expiry should be either a number of days or a datetime.timedelta object """
        super(CachedRequest, self).__init__(*args, **kwargs)
        if not isinstance(expiry, datetime.timedelta):
            expiry = datetime.timedelta(expiry)
        self.expiry = expiry
        self._data = None
        self._cachetime = None

    def _get_cache_dir(self):
        path = os.path.join(pywikibot.config2.base_dir, 'apicache')
        self._make_dir(path)
        return path

    def _make_dir(self, dir):
        try:
            os.makedirs(dir)
        except OSError:
            # directory already exists
            pass

    def _create_file_name(self):
        return hashlib.sha256(str(self.site) + str(self)).hexdigest()

    def _cachefile_path(self):
        return os.path.join(self._get_cache_dir(), self._create_file_name())

    def _expired(self, dt):
        return dt + self.expiry < datetime.datetime.now()

    def _load_cache(self):
        """ Returns whether the cache can be used """
        try:
            sitestr, selfstr, self._data, self._cachetime = pickle.load(open(self._cachefile_path()))
            assert(sitestr == str(self.site))
            assert(selfstr == str(self))
            if self._expired(self._cachetime):
                self._data = None
                return False
            return True
        except Exception:
            return False

    def _write_cache(self, data):
        """ writes data to self._cachefile_path() """
        data = [str(self.site), str(self), data, datetime.datetime.now()]
        pickle.dump(data, open(self._cachefile_path(), 'w'))

    def submit(self):
        cached_available = self._load_cache()
        if not cached_available:
            self._data = super(CachedRequest, self).submit()
            self._write_cache(self._data)
        return self._data

class QueryGenerator(object):
    """Base class for iterators that handle responses to API action=query.

    By default, the iterator will iterate each item in the query response,
    and use the query-continue element, if present, to continue iterating as
    long as the wiki returns additional values.  However, if the iterator's
    limit attribute is set to a positive int, the iterator will stop after
    iterating that many values. If limit is negative, the limit parameter
    will not be passed to the API at all.

    Most common query types are more efficiently handled by subclasses, but
    this class can be used directly for custom queries and miscellaneous
    types (such as "meta=...") that don't return the usual list of pages or
    links. See the API documentation for specific query options.

    """
    def __init__(self, **kwargs):
        """
        Constructor: kwargs are used to create a Request object;
        see that object's documentation for values. 'action'='query' is
        assumed.

        """
        if "action" in kwargs and "action" != "query":
            raise Error("%s: 'action' must be 'query', not %s"
                        % (self.__class__.__name__, kwargs["query"]))
        else:
            kwargs["action"] = "query"
        try:
            self.site = kwargs["site"]
        except KeyError:
            self.site = pywikibot.Site()
            kwargs["site"] = self.site
        # make sure request type is valid, and get limit key if any
        for modtype in ("generator", "list", "prop", "meta"):
            if modtype in kwargs:
                self.module = kwargs[modtype]
                break
        else:
            raise Error("%s: No query module name found in arguments."
                        % self.__class__.__name__)

        kwargs["indexpageids"] = ""  # always ask for list of pageids
        self.request = Request(**kwargs)
        self.prefix = None
        self.update_limit() # sets self.prefix
        if self.api_limit is not None and "generator" in kwargs:
            self.prefix = "g" + self.prefix
        self.limit = None
        self.query_limit = self.api_limit
        if "generator" in kwargs:
            self.resultkey = "pages"        # name of the "query" subelement key
        else:                               # to look for when iterating
            self.resultkey = self.module
        self.continuekey = self.module      # usually the query-continue key
                                            # is the same as the querymodule,
                                            # but not always

    @property
    def __modules(self):
        """
        Instance cache: hold the query data for paraminfo on
        querymodule=self.module at self.site

        """
        if not hasattr(self.site, "_modules"):
            setattr(self.site, "_modules", dict())
        return self.site._modules

    @__modules.deleter
    def __modules(self):
        """Delete the instance cache - maybe we don't need it"""
        if hasattr(self.site, "_modules"):
            del self.site._modules

    @property
    def _modules(self):
        """Query api on self.site for paraminfo on querymodule=self.module"""
        if not set(self.module.split('|')) <= set(self.__modules.keys()):
            paramreq = CachedRequest(expiry=config.API_config_expiry,
                                     site=self.site, action="paraminfo",
                                     querymodules=self.module)
            data = paramreq.submit()
            assert "paraminfo" in data
            assert "querymodules" in data["paraminfo"]
            assert len(data["paraminfo"]["querymodules"]) == 1 + self.module.count("|")
            for paraminfo in data["paraminfo"]["querymodules"]:
                assert paraminfo["name"] in self.module
                if "missing" in paraminfo:
                    raise Error("Invalid query module name '%s'." % self.module)
                self.__modules[paraminfo["name"]] = paraminfo
        _modules = {}
        for m in self.module.split('|'):
            _modules[m] = self.__modules[m]
        return _modules

    def set_query_increment(self, value):
        """Set the maximum number of items to be retrieved per API query.

        If not called, the default is to ask for "max" items and let the
        API decide how many to send.

        """
        limit = int(value)
        # don't update if limit is greater than maximum allowed by API
        self.update_limit()
        if self.api_limit is None:
            self.query_limit = limit
        else:
            self.query_limit = min(self.api_limit, limit)

    def set_maximum_items(self, value):
        """Set the maximum number of items to be retrieved from the wiki.

        If not called, most queries will continue as long as there is
        more data to be retrieved from the API.

        If set to -1 (or any negative value), the "limit" parameter will be
        omitted from the request. For some request types (such as
        prop=revisions), this is necessary to signal that only current
        revision is to be returned.

        """
        self.limit = int(value)

    def update_limit(self):
        """Set query limit for self.module based on api response"""

        self.api_limit = None
        for mod in self.module.split('|'):
            for param in self._modules[mod].get("parameters", []):
                if param["name"] == "limit":
                    if (self.site.logged_in()
                        and self.site.has_right('apihighlimits')):
                        self.api_limit = int(param["highmax"])
                    else:
                        self.api_limit = int(param["max"])
                    if self.prefix is None:
                        self.prefix = self._modules[mod]["prefix"]
                    pywikibot.debug(u"%s: Set query_limit to %i."
                                      % (self.__class__.__name__,
                                         self.api_limit),
                                    _logger)
                    return

    def set_namespace(self, namespaces):
        """Set a namespace filter on this query.

        @param namespaces: Either an int or a list of ints

        """
        if isinstance(namespaces, list):
            namespaces = "|".join(str(n) for n in namespaces)
        else:
            namespaces = str(namespaces)
        for mod in self.module.split('|'):
            for param in self._modules[mod].get("parameters", []):
                if param["name"] == "namespace":
                    self.request[self.prefix+"namespace"] = namespaces
                    return

    def __iter__(self):
        """Submit request and iterate the response based on self.resultkey

        Continues response as needed until limit (if any) is reached.

        """
        count = 0
        while True:
            if self.query_limit is not None:
                if self.limit is None:
                    new_limit = self.query_limit
                elif self.limit > 0:
                    new_limit = min(self.query_limit, self.limit - count)
                else:
                    new_limit = None
                if "rvprop" in self.request \
                        and "content" in self.request["rvprop"]:
                    # queries that retrieve page content have lower limits
                    # Note: although API allows up to 500 pages for content
                    #   queries, these sometimes result in server-side errors
                    #   so use 250 as a safer limit
                    new_limit = min(new_limit, self.api_limit // 10, 250)
                if new_limit is not None:
                    self.request[self.prefix+"limit"] = str(new_limit)
            if not hasattr(self, "data"):
                try:
                    self.data = self.request.submit()
                except Server504Error:
                    # server timeout, usually caused by request with high limit
                    old_limit = self.query_limit
                    if old_limit is None or old_limit < 2:
                        raise
                    pywikibot.log("Setting query limit to %s" % (old_limit // 2))
                    self.set_query_increment(old_limit // 2)
                    continue
            if not self.data or not isinstance(self.data, dict):
                pywikibot.debug(
                    u"%s: stopped iteration because no dict retrieved from api."
                        % self.__class__.__name__,
                    _logger)
                return
            if "query" not in self.data:
                pywikibot.debug(
u"%s: stopped iteration because 'query' not found in api response."
                        % (self.__class__.__name__, self.resultkey),
                    _logger)
                pywikibot.debug(unicode(self.data), _logger)
                return
            if self.resultkey in self.data["query"]:
                resultdata = self.data["query"][self.resultkey]
                if isinstance(resultdata, dict):
                    pywikibot.debug(u"%s received %s; limit=%s"
                                      % (self.__class__.__name__,
                                         resultdata.keys(),
                                         self.limit),
                                    _logger)
                    if "results" in resultdata:
                        resultdata = resultdata["results"]
                    elif "pageids" in self.data["query"]:
                        # this ensures that page data will be iterated
                        # in the same order as received from server
                        resultdata = [resultdata[k]
                                      for k in self.data["query"]["pageids"]]
                    else:
                        resultdata = [resultdata[k]
                                      for k in sorted(resultdata.keys())]
                else:
                    pywikibot.debug(u"%s received %s; limit=%s"
                                      % (self.__class__.__name__,
                                         resultdata,
                                         self.limit),
                                    _logger)
                if "normalized" in self.data["query"]:
                    self.normalized = dict((item['to'], item['from'])
                                          for item in
                                          self.data["query"]["normalized"])
                else:
                    self.normalized = {}
                for item in resultdata:
                    yield self.result(item)
                    count += 1
                    if self.limit > 0 and count >= self.limit:
                        return
            if not "query-continue" in self.data:
                return
            if not self.continuekey in self.data["query-continue"]:
                pywikibot.log(
                    u"Missing '%s' key in ['query-continue'] value."
                      % self.continuekey)
                return
            update = self.data["query-continue"][self.continuekey]
            for key, value in update.iteritems():
                # query-continue can return ints
                if isinstance(value, int):
                    value = str(value)
                self.request[key] = value
            del self.data # a new request with query-continue is needed

    def result(self, data):
        """Process result data as needed for particular subclass."""
        return data

class PageGenerator(QueryGenerator):
    """Iterator for response to a request of type action=query&generator=foo.

    This class can be used for any of the query types that are listed in the
    API documentation as being able to be used as a generator.  Instances of
    this class iterate Page objects.

    """
    def __init__(self, generator, g_content=False, **kwargs):
        """
        Required and optional parameters are as for C{Request}, except that
        action=query is assumed and generator is required.

        @param generator: the "generator=" type from api.php
        @type generator: str
        @param g_content: if True, retrieve the contents of the current
            version of each Page (default False)

        """
        # get some basic information about every page generated
        if 'prop' in kwargs:
            kwargs['prop'] += "|info|imageinfo|categoryinfo"
        else:
            kwargs['prop'] = 'info|imageinfo|categoryinfo'
        if g_content:
            # retrieve the current revision
            kwargs['prop'] += "|revisions"
            if "rvprop" in kwargs:
                kwargs["rvprop"] += "ids|timestamp|flags|comment|user|content"
            else:
                kwargs["rvprop"] = "ids|timestamp|flags|comment|user|content"
        if "inprop" in kwargs:
            if "protection" not in kwargs["inprop"]:
                kwargs["inprop"] += "|protection"
        else:
            kwargs['inprop'] = 'protection'
        if "iiprop" in kwargs:
            kwargs["iiprop"] += 'timestamp|user|comment|url|size|sha1|metadata'
        else:
            kwargs['iiprop'] = 'timestamp|user|comment|url|size|sha1|metadata'
        QueryGenerator.__init__(self, generator=generator, **kwargs)
        self.resultkey = "pages" # element to look for in result

    def result(self, pagedata):
        """Convert page dict entry from api to Page object.

        This can be overridden in subclasses to return a different type
        of object.

        """
        p = pywikibot.Page(self.site, pagedata['title'], pagedata['ns'])
        update_page(p, pagedata)
        return p

class CategoryPageGenerator(PageGenerator):
    """Like PageGenerator, but yields Category objects instead of Pages."""

    def result(self, pagedata):
        p = PageGenerator.result(self, pagedata)
        return pywikibot.Category(p)

class ImagePageGenerator(PageGenerator):
    """Like PageGenerator, but yields ImagePage objects instead of Pages."""

    def result(self, pagedata):
        p = PageGenerator.result(self, pagedata)
        image = pywikibot.ImagePage(p)
        if 'imageinfo' in pagedata:
            image._imageinfo = pagedata['imageinfo'][0]
        return image

class PropertyGenerator(QueryGenerator):
    """Iterator for queries of type action=query&property=...

    See the API documentation for types of page properties that can be
    queried.

    This iterator yields one or more dict object(s) corresponding
    to each "page" item(s) from the API response; the calling module has to
    decide what to do with the contents of the dict. There will be one
    dict for each page queried via a titles= or ids= parameter (which must
    be supplied when instantiating this class).

    """
    def __init__(self, prop, **kwargs):
        """
        Required and optional parameters are as for C{Request}, except that
        action=query is assumed and prop is required.

        @param prop: the "property=" type from api.php
        @type prop: str

        """
        QueryGenerator.__init__(self, prop=prop, **kwargs)
        self.resultkey = "pages"

class ListGenerator(QueryGenerator):
    """Iterator for queries of type action=query&list=...

    See the API documentation for types of lists that can be queried.  Lists
    include both side-wide information (such as 'allpages') and page-specific
    information (such as 'backlinks').

    This iterator yields a dict object for each member of the list returned
    by the API, with the format of the dict depending on the particular list
    command used.  For those lists that contain page information, it may be
    easier to use the PageGenerator class instead, as that will convert the
    returned information into a Page object.

    """
    def __init__(self, listaction, **kwargs):
        """
        Required and optional parameters are as for C{Request}, except that
        action=query is assumed and listaction is required.

        @param listaction: the "list=" type from api.php
        @type listaction: str

        """
        QueryGenerator.__init__(self, list=listaction, **kwargs)

class LogEntryListGenerator(ListGenerator):
    """
    Like ListGenerator, but specialized for listaction="logevents" :
    yields LogEntry objects instead of dicts.
    """
    def __init__(self, logtype=None, **kwargs):
        ListGenerator.__init__(self, "logevents", **kwargs)

        import logentries
        self.entryFactory = logentries.LogEntryFactory(logtype)

    def result(self, pagedata):
        return self.entryFactory.create(pagedata)

class LoginManager(login.LoginManager):
    """Supplies getCookie() method to use API interface."""
    def getCookie(self, remember=True, captchaId=None, captchaAnswer=None):
        """Login to the site.

        Parameters are all ignored.

        Returns cookie data if succesful, None otherwise.

        """
        if hasattr(self, '_waituntil'):
            if datetime.datetime.now() < self._waituntil:
                diff = self._waituntil - datetime.datetime.now()
                pywikibot.warning(u"Too many tries, waiting %s seconds before retrying."
                                    % diff.seconds)
                time.sleep(diff.seconds)
        login_request = Request(site=self.site,
                                action="login",
                                lgname=self.username,
                                lgpassword=self.password
                               )
        self.site._loginstatus = -2
        while True:
            login_result = login_request.submit()
            if u"login" not in login_result:
                raise RuntimeError("API login response does not have 'login' key.")
            if login_result['login']['result'] == "Success":
                prefix = login_result['login']['cookieprefix']
                cookies = []
                for key in ('Token', 'UserID', 'UserName'):
                    cookies.append("%s%s=%s"
                                   % (prefix, key,
                                      login_result['login']['lg'+key.lower()]))
                self.username = login_result['login']['lgusername']
                return "\n".join(cookies)
            elif login_result['login']['result'] == "NeedToken":
                token = login_result['login']['token']
                login_request["lgtoken"] = token
                continue
            elif login_result['login']['result'] == "Throttled":
                self._waituntil = datetime.datetime.now() \
                                  + datetime.timedelta(seconds=int(
                                                login_result["login"]["wait"])
                                              )
                break
            else:
                break
        raise APIError(code=login_result["login"]["result"], info="")

    def storecookiedata(self, data):
        # ignore data; cookies are set by threadedhttp module
        pywikibot.cookie_jar.save()

def update_page(page, pagedict):
    """Update attributes of Page object page, based on query data in pagedict

    @param page: object to be updated
    @type page: Page
    @param pagedict: the contents of a "page" element of a query response
    @type pagedict: dict

    """
    if "pageid" in pagedict:
        page._pageid = int(pagedict['pageid'])
    elif "missing" in pagedict:
        page._pageid = 0    # Non-existent page
    else:
        raise AssertionError(
            "Page %s has neither 'pageid' nor 'missing' attribute"
             % pagedict['title'])
    page._isredir = 'redirect' in pagedict
    if 'touched' in pagedict:
        page._timestamp = pagedict['touched']
    if 'protection' in pagedict:
        page._protection = {}
        for item in pagedict['protection']:
            page._protection[item['type']] = item['level'], item['expiry']
    if 'revisions' in pagedict:
        for rev in pagedict['revisions']:
            revision = pywikibot.page.Revision(
                revid=rev['revid'],
                timestamp=pywikibot.Timestamp.fromISOformat(rev['timestamp']),
                user=rev.get('user', u''),
                anon='anon' in rev,
                comment=rev.get('comment',  u''),
                minor='minor' in rev,
                text=rev.get('*', None)
              )
            page._revisions[revision.revid] = revision

    if 'lastrevid' in pagedict:
        page._revid = pagedict['lastrevid']
        if page._revid in page._revisions:
            page._text = page._revisions[page._revid].text

    if "categoryinfo" in pagedict:
        page._catinfo = pagedict["categoryinfo"]

    if "templates" in pagedict:
        page._templates = [ pywikibot.Page(page.site, tl['title'])
                                for tl in pagedict['templates'] ]

    if "langlinks" in pagedict:
        links = []
        for ll in pagedict["langlinks"]:
            link = pywikibot.Link.langlinkUnsafe(ll['lang'],
                                                 ll['*'],
                                                 source=page.site)
            links.append(link)
        page._langlinks = links

    if "coordinates" in pagedict:
        coords = []
        for co in pagedict['coordinates']:
            coord = pywikibot.Coordinate(lat=co['lat'],
                                         lon=co['lon'],
                                         typ=co.get('type', ''),
                                         name=co['name'],
                                         dim=co['dim'],
                                         globe=co.get('globe', 'earth'),  # See [[gerrit:67886]]
                                         )
            coords.append(coord)
        page._coords = coords

if __name__ == "__main__":
    from pywikibot import Site, logging
    logging.getLogger("pywiki.data.api").setLevel(logging.DEBUG)
    mysite = Site("en", "wikipedia")
    pywikibot.output(u"starting test....")
    def _test():
        import doctest
        doctest.testmod()
    try:
        _test()
    finally:
        pywikibot.stopme()


text:
package com.example.chengqi.mycoderepo.containers;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.SimpleAdapter;

import com.example.chengqi.mycoderepo.R;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ContainerGridViewActivity extends AppCompatActivity {
    private static final String LOG_TAG = "GridViewActivity";
    private GridView gview;
    private List<Map<String, Object>> data_list;
    private SimpleAdapter sim_adapter;
    // 图片封装为一个数组
    private int[] icon = { R.drawable.address_book, R.drawable.calendar,
            R.drawable.camera, R.drawable.clock, R.drawable.games_control,
            R.drawable.messenger, R.drawable.ringtone, R.drawable.settings,
            R.drawable.speech_balloon, R.drawable.weather, R.drawable.world,
            R.drawable.youtube };
    private String[] iconName = { "通讯录", "日历", "照相机", "时钟", "游戏", "短信", "铃声",
            "设置", "语音", "天气", "浏览器", "视频" };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_container_grid_view);
        gview = (GridView) findViewById(R.id.gridView);
        //新建List
        data_list = new ArrayList<Map<String, Object>>();
        //获取数据
        getData();
        //新建适配器
        String [] from ={"image","text"};
        int [] to = {R.id.image,R.id.text};
        sim_adapter = new SimpleAdapter(this, data_list, R.layout.item, from, to);
        //配置适配器
        gview.setAdapter(sim_adapter);
        gview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.d(LOG_TAG, "item click");
            }
        });
    }

    public List<Map<String, Object>> getData(){
        //cion和iconName的长度是相同的,这里任选其一都可以
        for(int i=0;i<icon.length;i++){
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("image", icon[i]);
            map.put("text", iconName[i]);
            data_list.add(map);
        }

        return data_list;
    }
}

redpajama_set_name: RedPajamaGithub


text:
CDXUTDialogResourceManager          g_DialogResourceManager; // manager for shared resources of dialogs
CModelViewerCamera                  g_Camera;                   // A model viewing camera
CDXUTDirectionWidget                g_LightControl;
CD3DSettingsDlg                     g_D3DSettingsDlg;           // Device settings dialog
CDXUTDialog                         g_HUD;                      // manages the 3D
CDXUTDialog                         g_SampleUI;                 // dialog for sample specific controls

// Resources
CDXUTTextHelper*                    g_pTxtHelper = NULL;
ID3DX10Font*                        g_pFont10 = NULL;
ID3DX10Sprite*                      g_pSprite10 = NULL;
ID3D10InputLayout*                  g_pPatchLayout = NULL;
ID3D10InputLayout*                  g_pRegularPatchLayout = NULL;
ID3D10InputLayout*                  g_pPrecalcPatchLayout = NULL;
ID3D10Effect*                       g_pEffect10 = NULL;
ID3D10Effect*                       g_pEffectConvert10 = NULL;
ID3D10Buffer*                       g_pPatchVB[MAX_DIVS-1] = {0};
ID3D10Buffer*                       g_pPatchIB[MAX_DIVS-1] = {0};

// SubD10.fx techniques
ID3D10EffectTechnique*              g_pTechCurrent = NULL;
ID3D10EffectTechnique*              g_pTechRenderScene = NULL;
ID3D10EffectTechnique*              g_pTechRenderSceneTan = NULL;
ID3D10EffectTechnique*              g_pTechRenderSceneBump = NULL;
ID3D10EffectTechnique*              g_pTechRenderSceneBumpTan = NULL;

// SubD10.fx variables
ID3D10EffectShaderResourceVariable* g_ptxHeight = NULL;
ID3D10EffectShaderResourceVariable* g_pbufPatchesB = NULL;
ID3D10EffectShaderResourceVariable* g_pbufPatchesUV = NULL;
ID3D10EffectShaderResourceVariable* g_pbufControlPointsUV = NULL;
ID3D10EffectMatrixVariable*         g_pmWorldViewProjection = NULL;
ID3D10EffectMatrixVariable*         g_pmWorld = NULL;
ID3D10EffectScalarVariable*         g_pfTime = NULL;
ID3D10EffectVectorVariable*         g_pvEyeDir = NULL;
ID3D10EffectScalarVariable*         g_pfHeightAmount = NULL;
ID3D10EffectScalarVariable*         g_pPatchIDOffset = NULL;
ID3D10EffectVectorVariable*         g_pvPatches = NULL;
ID3D10EffectScalarVariable*         g_pPatchSize = NULL;
ID3D10EffectScalarVariable*         g_pInvPatchSize = NULL;
ID3D10EffectScalarVariable*         g_pPatchStride = NULL;
ID3D10EffectScalarVariable*         g_pVerticesPerPatch = NULL;
ID3D10EffectVectorVariable*         g_pPatchOffsets = NULL;

// SubDtoBezier.fx techniques
ID3D10EffectTechnique*              g_pSubDToBezierB = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierUV = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierB_Reg = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierUV_Reg = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierB_Skin = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierUV_Skin = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierB_Reg_Skin = NULL;
ID3D10EffectTechnique*              g_pSubDToBezierUV_Reg_Skin = NULL;

// SubDtoBezier.fx variables
ID3D10EffectScalarVariable*         g_pTanM = NULL;
ID3D10EffectScalarVariable*         g_pfCi = NULL;
ID3D10EffectScalarVariable*         g_pfSkinTime = NULL;
ID3D10EffectMatrixVariable*         g_pmConstBoneWorld = NULL;
ID3D10EffectShaderResourceVariable* g_pbufPoints = NULL;
ID3D10EffectShaderResourceVariable* g_pbufBones = NULL;

// Control variables
int                                 g_iSubdivs = 8;                         // Startup subdivisions per side
bool                                g_bDrawWires = false;                   // Draw the mesh with wireframe overlay
bool                                g_bSeparatePasses = true;               // Do a separate pass for regular and extraordinary patches
bool                                g_bConvertEveryFrame = true;            // Convert from subd to bezier every frame
bool                                g_bAttenuateBasedOnDistance = true;     // Attenuate the tesselation level based upon distance to the camera
bool                                g_bRenderSkinned = false;               // Skin the meshes (GPU only)
float                               g_fHeightAmount = 2.0f;                 // The height amount for displacement mapping
const float                         g_fFarPlane = 500.0f;                   // Distance to the far plane

// Pre-tessellated, pre-computed parametric patch vertex
struct PATCH_VERTEX
{
    D3DXVECTOR2 m_UV;
    D3DXVECTOR4 m_BasisU;
    D3DXVECTOR4 m_BasisV;
    D3DXVECTOR4 m_dBasisU;
    D3DXVECTOR4 m_dBasisV;
};

// Mesh structures
struct SUBDMESH_DESC
{
    WCHAR   m_szFileName[MAX_PATH];
    WCHAR   m_szHeightMap[MAX_PATH];
    D3DXVECTOR3 m_vPosition;
    D3DXVECTOR3 m_vScale;
    D3DXVECTOR3 m_vSpineDir;
};

SUBDMESH_DESC g_MeshDesc[] =
{
    { L"SubD10\\monsterfrog_mapped_00.obj",  L"SubD10\\monsterfrog.dds", D3DXVECTOR3( 0, 0, 0 ),
        D3DXVECTOR3( 0.5f, 0.5f, 0.5f ), D3DXVECTOR3( 0, 0, 1 ) },
    { L"SubD10\\bigguy_00.obj",              L"",                        D3DXVECTOR3( -50, 0, 0 ),
        D3DXVECTOR3( 1, 1, 1 ),          D3DXVECTOR3( 0, 1, 0 ) },
    { L"SubD10\\guy3.obj",                   L"",                        D3DXVECTOR3( 50, 0, 0 ),
        D3DXVECTOR3( 1, 1, 1 ),          D3DXVECTOR3( 0, 1, 0 ) },
    { L"SubD10\\subdbox.obj",                L"",                        D3DXVECTOR3( 0, 0, 50 ),
        D3DXVECTOR3( 1, 1, 1 ),          D3DXVECTOR3( 0, 0, 1 ) },
    { L"SubD10\\testshape.obj",              L"",                        D3DXVECTOR3( -50, 0, 50 ),
        D3DXVECTOR3( 1, 1, 1 ),          D3DXVECTOR3( 0, 1, 0 ) },
    { L"SubD10\\cube.obj",                   L"",                        D3DXVECTOR3( 50, 0, 50 ),
        D3DXVECTOR3( 1, 1, 1 ),          D3DXVECTOR3( 0, 1, 0 ) },
    { L"SubD10\\shipquads.obj",              L"",                        D3DXVECTOR3( -50, 0, -50 ),
        D3DXVECTOR3( 1, 1, 1 ),          D3DXVECTOR3( 1, 0, 0 ) },
    { L"SubD10\\tree.obj",                   L"",                        D3DXVECTOR3( 0, 0, -50 ),
        D3DXVECTOR3( 2, 2, 2 ),          D3DXVECTOR3( 0, 1, 0 ) },
    { L"SubD10\\head.obj",                   L"",                        D3DXVECTOR3( 50, 0, -50 ),
        D3DXVECTOR3( 2, 2, 2 ),          D3DXVECTOR3( 0, 1, 0 ) },
};

CSubDMesh g_SubDMesh[ ARRAYSIZE( g_MeshDesc ) ];
UINT                                g_iNumSubDMeshes = ARRAYSIZE( g_MeshDesc );

//--------------------------------------------------------------------------------------
// UI control IDs
//--------------------------------------------------------------------------------------
#define IDC_TOGGLEFULLSCREEN      1
#define IDC_TOGGLEREF             3
#define IDC_CHANGEDEVICE          4

#define IDC_PATCH_SUBDIVS         5
#define IDC_PATCH_SUBDIVS_STATIC  6
#define IDC_BUMP_HEIGHT	          7
#define IDC_BUMP_HEIGHT_STATIC    8
#define IDC_TOGGLE_LINES          9
#define IDC_TECH_STATIC           10
#define IDC_TECH                  11
#define IDC_SEPARATE_PASSES       12
#define IDC_CONVERT_PER_FRAME     13
#define IDC_DIST_ATTEN            14
#define IDC_SKIN                  15
#define IDC_TOGGLEWARP            16
//--------------------------------------------------------------------------------------
// Forward declarations
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext );
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
                          void* pUserContext );
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );

HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
                                      void* pUserContext );
HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void CALLBACK OnD3D10ReleasingSwapChain( void* pUserContext );
void CALLBACK OnD3D10DestroyDevice( void* pUserContext );
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );

void InitApp();
void RenderText();
UINT SetPatchConstants( ID3D10Device* pd3dDevice, UINT iSubdivs );
void UpdateSubDPatches( ID3D10Device* pd3dDevice, CSubDMesh* pMesh );
void ConvertFromSubDToBezier( ID3D10Device* pd3dDevice, CSubDMesh* pMesh );
void FillTables();
HRESULT CreatePatchVBsIBs( ID3D10Device* pd3dDevice );

//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // DXUT will create and use the best device (either D3D9 or D3D10)
    // that is available on the system depending on which D3D callbacks are set below

    // Set DXUT callbacks
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameMove( OnFrameMove );

    DXUTSetCallbackD3D10DeviceCreated( OnD3D10CreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnD3D10ResizedSwapChain );
    DXUTSetCallbackD3D10FrameRender( OnD3D10FrameRender );
    DXUTSetCallbackD3D10SwapChainReleasing( OnD3D10ReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnD3D10DestroyDevice );

    InitApp();
    DXUTInit( true, true, NULL ); // Parse the command line, show msgboxes on error, no extra command line params
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"SubD10" );
    DXUTCreateDevice( true, 1024, 768 );
    DXUTMainLoop(); // Enter into the DXUT render loop

    return DXUTGetExitCode();
}

//--------------------------------------------------------------------------------------
// Initialize the app
//--------------------------------------------------------------------------------------
void InitApp()
{
    g_LightControl.SetLightDirection( D3DXVECTOR3( 0, 0, -1 ) );

    // Initialize dialogs
    g_D3DSettingsDlg.Init( &g_DialogResourceManager );
    g_HUD.Init( &g_DialogResourceManager );
    g_SampleUI.Init( &g_DialogResourceManager );

    g_HUD.SetCallback( OnGUIEvent ); int iY = 10;
    g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 );
    g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22, VK_F2 );
    g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22, VK_F3 );
    g_HUD.AddButton( IDC_TOGGLEWARP, L"Toggle WARP (F4)", 35, iY += 24, 125, 22, VK_F4 );

    g_SampleUI.SetCallback( OnGUIEvent ); iY = 10;

    WCHAR sz[100];
    iY += 24;
    swprintf_s( sz, 100, L"Patch Divisions: %d", g_iSubdivs );
    g_SampleUI.AddStatic( IDC_PATCH_SUBDIVS_STATIC, sz, 35, iY += 24, 125, 22 );
    g_SampleUI.AddSlider( IDC_PATCH_SUBDIVS, 50, iY += 24, 100, 22, 1, MAX_DIVS - 1, g_iSubdivs );

    swprintf_s( sz, 100, L"BumpHeight: %.4f", g_fHeightAmount );
    g_SampleUI.AddStatic( IDC_BUMP_HEIGHT_STATIC, sz, 35, iY += 24, 125, 22 );
    g_SampleUI.AddSlider( IDC_BUMP_HEIGHT, 50, iY += 24, 100, 22, 0, MAX_BUMP, ( int )( 1000.0f * g_fHeightAmount ) );

    iY += 24;
    g_SampleUI.AddStatic( IDC_TECH_STATIC, L"Technique", 35, iY += 24, 125, 22 );
    CDXUTComboBox* pComboBox = NULL;
    g_SampleUI.AddComboBox( IDC_TECH, 0, iY += 24, 180, 22, 0, false, &pComboBox );
    pComboBox->SetDropHeight( 120 );

    iY += 24;
    g_SampleUI.AddCheckBox( IDC_TOGGLE_LINES, L"Toggle Wires", 35, iY += 24, 125, 22, g_bDrawWires );
    g_SampleUI.AddCheckBox( IDC_SEPARATE_PASSES, L"Separate Reg/Extra", 35, iY += 24, 125, 22, g_bSeparatePasses );
    g_SampleUI.AddCheckBox( IDC_CONVERT_PER_FRAME, L"Convert Every Frame", 35, iY += 24, 125, 22,
                            g_bConvertEveryFrame );
    g_SampleUI.AddCheckBox( IDC_DIST_ATTEN, L"Distance Attenuate", 35, iY += 24, 125, 22,
                            g_bAttenuateBasedOnDistance );
    g_SampleUI.AddCheckBox( IDC_SKIN, L"Skin Meshes", 35, iY += 24, 125, 22, g_bRenderSkinned );

    // Setup the camera's view parameters
    D3DXVECTOR3 vecEye( -70.0f, 20.0f, 70.0f );
    D3DXVECTOR3 vecAt ( 0.0f, 0.0f, 0.0f );
    g_Camera.SetViewParams( &vecEye, &vecAt );

}

//--------------------------------------------------------------------------------------
// Called right before creating a D3D9 or D3D10 device, allowing the app to modify the device settings as needed
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
    // Disable vsync
    pDeviceSettings->d3d10.SyncInterval = 0;
    g_D3DSettingsDlg.GetDialogControl()->GetComboBox( DXUTSETTINGSDLG_PRESENT_INTERVAL )->SetEnabled( false );

    // For the first device created if its a REF device, optionally display a warning dialog box
    static bool s_bFirstTime = true;
    if( s_bFirstTime )
    {
        s_bFirstTime = false;
        if( ( DXUT_D3D9_DEVICE == pDeviceSettings->ver && pDeviceSettings->d3d9.DeviceType == D3DDEVTYPE_REF ) ||
            ( DXUT_D3D10_DEVICE == pDeviceSettings->ver &&
              pDeviceSettings->d3d10.DriverType == D3D10_DRIVER_TYPE_REFERENCE ) )
            DXUTDisplaySwitchingToREFWarning( pDeviceSettings->ver );
    }

    return true;
}

//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
    // Update the camera's position based on user input
    g_Camera.FrameMove( fElapsedTime );

    // Randomly animate the skinned meshes
    for( UINT i = 0; i < g_iNumSubDMeshes; i++ )
    {
        for( UINT b = 0; b < NUM_BONES; b++ )
        {
            D3DXVECTOR3 vRotate(0,0,0);

            vRotate.x = D3DXToRadian( 20.0f ) * sinf( ( float )fTime * 0.5f * ( i + 1 ) );
            vRotate.y = D3DXToRadian( 15.0f ) * sinf( ( float )fTime * 0.75f * ( i + 1 ) );

            vRotate.x *= ( b ) / 2.0f;
            vRotate.y *= ( float )( b % 2 );

            if( i % 2 == 1 )
            {
                float temp = vRotate.x;
                vRotate.x = vRotate.y;
                vRotate.y = temp;
            }

            g_SubDMesh[i].RotateBone( b, vRotate );
        }
        g_SubDMesh[i].CreateSkinningMatrices();
    }
}

//--------------------------------------------------------------------------------------
// Render the help and statistics text
//--------------------------------------------------------------------------------------
void RenderText()
{
    g_pTxtHelper->Begin();
    g_pTxtHelper->SetInsertionPos( 2, 0 );
    g_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
    g_pTxtHelper->DrawTextLine( DXUTGetFrameStats( DXUTIsVsyncEnabled() ) );
    g_pTxtHelper->DrawTextLine( DXUTGetDeviceStats() );

    g_pTxtHelper->End();
}

//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
                          void* pUserContext )
{
    // Pass messages to dialog resource manager calls so GUI state is updated correctly
    *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    // Pass messages to settings dialog if its active
    if( g_D3DSettingsDlg.IsActive() )
    {
        g_D3DSettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
        return 0;
    }

    // Give the dialogs a chance to handle the message first
    *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;
    *pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    g_LightControl.HandleMessages( hWnd, uMsg, wParam, lParam );

    // Pass all remaining windows messages to camera so it can respond to user input
    g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );

    return 0;
}

//--------------------------------------------------------------------------------------
// Handle key presses
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
}

//--------------------------------------------------------------------------------------
// Handles the GUI events
//--------------------------------------------------------------------------------------
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
    switch( nControlID )
    {
            // Standard DXUT controls
        case IDC_TOGGLEFULLSCREEN:
            DXUTToggleFullScreen(); break;
        case IDC_TOGGLEREF:
            DXUTToggleREF(); break;
        case IDC_CHANGEDEVICE:
            g_D3DSettingsDlg.SetActive( !g_D3DSettingsDlg.IsActive() ); break;
        case IDC_TOGGLEWARP:
            DXUTToggleWARP(); break;
            // Custom app controls
        case IDC_PATCH_SUBDIVS:
        {
            g_iSubdivs = g_SampleUI.GetSlider( IDC_PATCH_SUBDIVS )->GetValue();

            WCHAR sz[100];
            swprintf_s( sz, 100, L"Patch Divisions: %d", g_iSubdivs );
            g_SampleUI.GetStatic( IDC_PATCH_SUBDIVS_STATIC )->SetText( sz );
        }
            break;
        case IDC_BUMP_HEIGHT:
        {
            g_fHeightAmount = ( float )g_SampleUI.GetSlider( IDC_BUMP_HEIGHT )->GetValue() / 1000.0f;

            WCHAR sz[100];
            swprintf_s( sz, 100, L"BumpHeight: %.4f", g_fHeightAmount );
            g_SampleUI.GetStatic( IDC_BUMP_HEIGHT_STATIC )->SetText( sz );

            g_pfHeightAmount->SetFloat( g_fHeightAmount );
        }
            break;
        case IDC_TOGGLE_LINES:
        {
            g_bDrawWires = g_SampleUI.GetCheckBox( IDC_TOGGLE_LINES )->GetChecked();
        }
            break;
        case IDC_TECH:
        {
            CDXUTComboBox* pComboBox = ( CDXUTComboBox* )pControl;
            g_pTechCurrent = ( ID3D10EffectTechnique* )pComboBox->GetSelectedData();
        }
            break;
        case IDC_SEPARATE_PASSES:
        {
            g_bSeparatePasses = !g_bSeparatePasses;
        }
            break;
        case IDC_CONVERT_PER_FRAME:
        {
            g_bConvertEveryFrame = !g_bConvertEveryFrame;
        }
            break;
        case IDC_DIST_ATTEN:
        {
            g_bAttenuateBasedOnDistance = !g_bAttenuateBasedOnDistance;
        }
            break;
        case IDC_SKIN:
        {
            g_bRenderSkinned = !g_bRenderSkinned;
        }
            break;

    }

}

//--------------------------------------------------------------------------------------
// Create any D3D10 resources that aren't dependant on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
                                      void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnD3D10CreateDevice( pd3dDevice ) );
    V_RETURN( g_D3DSettingsDlg.OnD3D10CreateDevice( pd3dDevice ) );
    V_RETURN( D3DX10CreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET,
                                OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
                                L"Arial", &g_pFont10 ) );
    V_RETURN( D3DX10CreateSprite( pd3dDevice, 512, &g_pSprite10 ) );
    g_pTxtHelper = new CDXUTTextHelper( NULL, NULL, g_pFont10, g_pSprite10, 15 );

    V_RETURN( CDXUTDirectionWidget::StaticOnD3D10CreateDevice( pd3dDevice ) );

    DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows
    // the shaders to be optimized and to run exactly the way they will run in
    // the release configuration of this program.
    dwShaderFlags |= D3D10_SHADER_DEBUG;
    #endif

    // Read the D3DX effect file
    WCHAR str[MAX_PATH];

    // Setup our defines so that the FX file is in sync with the .cpp file
    char strPoints[MAX_PATH];
    sprintf_s( strPoints, ARRAYSIZE( strPoints ), "%d", MAX_POINTS );
    char strStride[MAX_PATH];
    sprintf_s( strStride, ARRAYSIZE( strPoints ), "%d", PATCH_STRIDE );

    D3D10_SHADER_MACRO macros[] =
    {
        { "MAX_POINTS", strPoints },
        { "PATCH_STRIDE", strStride },
        { NULL, NULL }
    };

    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"SubDtoBezier.fx" ) );
    V_RETURN( D3DX10CreateEffectFromFile( str, macros, NULL, "fx_4_0", dwShaderFlags, 0, pd3dDevice, NULL,
                                          NULL, &g_pEffectConvert10, NULL, NULL ) );

    // Setup our defines so that the FX file is in sync with the .cpp file
    D3D10_SHADER_MACRO macros2[] =
    {
        { "PATCH_STRIDE", strStride },
        { NULL, NULL }
    };

    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"SubD10.fx" ) );
    V_RETURN( D3DX10CreateEffectFromFile( str, macros2, NULL, "fx_4_0", dwShaderFlags, 0, pd3dDevice, NULL,
                                          NULL, &g_pEffect10, NULL, NULL ) );

    // Obtain technique objects
    g_pTechRenderScene = g_pEffect10->GetTechniqueByName( "RenderScene" );
    g_pTechRenderSceneTan = g_pEffect10->GetTechniqueByName( "RenderSceneTan" );
    g_pTechRenderSceneBump = g_pEffect10->GetTechniqueByName( "RenderSceneBump" );
    g_pTechRenderSceneBumpTan = g_pEffect10->GetTechniqueByName( "RenderSceneBumpTan" );

    CDXUTComboBox* pComboBox = g_SampleUI.GetComboBox( IDC_TECH );
    pComboBox->AddItem( L"RenderPatch", ( void* )g_pTechRenderScene );
    pComboBox->AddItem( L"RenderPatchTan", ( void* )g_pTechRenderSceneTan );
    pComboBox->AddItem( L"RenderBump", ( void* )g_pTechRenderSceneBump );
    pComboBox->AddItem( L"RenderBumpTan", ( void* )g_pTechRenderSceneBumpTan );

    g_pTechCurrent = g_pTechRenderSceneBumpTan;
    pComboBox->SetSelectedByIndex( 3 );

    // Obtain variables for sub10.fx
    g_ptxHeight = g_pEffect10->GetVariableByName( "g_txHeight" )->AsShaderResource();
    g_pbufPatchesB = g_pEffect10->GetVariableByName( "g_bufPatchesB" )->AsShaderResource();
    g_pbufPatchesUV = g_pEffect10->GetVariableByName( "g_bufPatchesUV" )->AsShaderResource();
    g_pbufControlPointsUV = g_pEffect10->GetVariableByName( "g_bufControlPointsUV" )->AsShaderResource();
    g_pmWorldViewProjection = g_pEffect10->GetVariableByName( "g_mWorldViewProjection" )->AsMatrix();
    g_pmWorld = g_pEffect10->GetVariableByName( "g_mWorld" )->AsMatrix();
    g_pfTime = g_pEffect10->GetVariableByName( "g_fTime" )->AsScalar();
    g_pvEyeDir = g_pEffect10->GetVariableByName( "g_vEyeDir" )->AsVector();
    g_pfHeightAmount = g_pEffect10->GetVariableByName( "g_fHeightAmount" )->AsScalar();
    g_pPatchIDOffset = g_pEffect10->GetVariableByName( "g_PatchIDOffset" )->AsScalar();

    g_pPatchSize = g_pEffect10->GetVariableByName( "g_PatchSize" )->AsScalar();
    g_pInvPatchSize = g_pEffect10->GetVariableByName( "g_InvPatchSize" )->AsScalar();
    g_pPatchStride = g_pEffect10->GetVariableByName( "g_PatchStride" )->AsScalar();
    g_pVerticesPerPatch = g_pEffect10->GetVariableByName( "g_VerticesPerPatch" )->AsScalar();
    g_pPatchOffsets = g_pEffect10->GetVariableByName( "g_PatchOffsets" )->AsVector();
    g_pvPatches = g_pEffect10->GetVariableByName( "g_Patches" )->AsVector();

    // Obtain variables for subdtobezier.fx
    g_pSubDToBezierB = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierB_Extra" );
    g_pSubDToBezierUV = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierUV_Extra" );
    g_pSubDToBezierB_Reg = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierB_Reg" );
    g_pSubDToBezierUV_Reg = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierUV_Reg" );

    g_pSubDToBezierB_Skin = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierB_Extra_Skin" );
    g_pSubDToBezierUV_Skin = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierUV_Extra_Skin" );
    g_pSubDToBezierB_Reg_Skin = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierB_Reg_Skin" );
    g_pSubDToBezierUV_Reg_Skin = g_pEffectConvert10->GetTechniqueByName( "SubDToBezierUV_Reg_Skin" );

    g_pbufPoints = g_pEffectConvert10->GetVariableByName( "g_bufPoints" )->AsShaderResource();
    g_pbufBones = g_pEffectConvert10->GetVariableByName( "g_bufBones" )->AsShaderResource();

    g_pTanM = g_pEffectConvert10->GetVariableByName( "g_TanM" )->AsScalar();
    g_pfCi = g_pEffectConvert10->GetVariableByName( "g_fCi" )->AsScalar();
    g_pfSkinTime = g_pEffectConvert10->GetVariableByName( "g_fTime" )->AsScalar();
    g_pmConstBoneWorld = g_pEffectConvert10->GetVariableByName( "g_mConstBoneWorld" )->AsMatrix();

    // Using pre-tesselated patches.  Create them here.
    V_RETURN( CreatePatchVBsIBs( pd3dDevice ) );

    // Create out precalculated patch layout
    const D3D10_INPUT_ELEMENT_DESC precalcpatchlayout[] =
    {
        { "PATCHUV",  0, DXGI_FORMAT_R32G32_FLOAT,       0, 0,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "BASISU",   0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "BASISV",   0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "DBASISU",  0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "DBASISV",  0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    D3D10_PASS_DESC PassDesc;
    g_pTechRenderScene->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    V_RETURN( pd3dDevice->CreateInputLayout( precalcpatchlayout, ARRAYSIZE( precalcpatchlayout ),
                                             PassDesc.pIAInputSignature,
                                             PassDesc.IAInputSignatureSize, &g_pPrecalcPatchLayout ) );

    // Create our vertex input layouts
    const D3D10_INPUT_ELEMENT_DESC patchlayout[] =
    {
        // Control Points
        { "CONTROLPOINTS",  0, DXGI_FORMAT_R32G32B32A32_UINT, 0, 0,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  1, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  2, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  3, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  4, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
#if (MAX_POINTS > 20 )
        { "CONTROLPOINTS",  5, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
#if (MAX_POINTS > 24 )
        { "CONTROLPOINTS",  6, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
#if (MAX_POINTS > 28 )
        { "CONTROLPOINTS",  7, DXGI_FORMAT_R32G32B32A32_UINT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
#endif
#endif
#endif
        // Valences and Prefixes
        { "VALENCES",       0, DXGI_FORMAT_R8G8B8A8_UINT,     0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "PREFIXES",       0, DXGI_FORMAT_R8G8B8A8_UINT,     0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    g_pSubDToBezierB->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    V_RETURN( pd3dDevice->CreateInputLayout( patchlayout, ARRAYSIZE( patchlayout ), PassDesc.pIAInputSignature,
                                             PassDesc.IAInputSignatureSize, &g_pPatchLayout ) );

    const D3D10_INPUT_ELEMENT_DESC regularpatchlayout[] =
    {
        // Control Points
        { "CONTROLPOINTS",  0,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  1,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  2,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  3,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  4,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  5,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  6,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  7,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  8,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  9,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  10, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  11, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  12, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  13, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  14, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "CONTROLPOINTS",  15, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT,
            D3D10_INPUT_PER_VERTEX_DATA, 0 }
    };
    g_pSubDToBezierB_Reg->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    V_RETURN( pd3dDevice->CreateInputLayout( regularpatchlayout, ARRAYSIZE( regularpatchlayout ),
                                             PassDesc.pIAInputSignature,
                                             PassDesc.IAInputSignatureSize, &g_pRegularPatchLayout ) );

    // Fill our helper/temporary tables
    FillTables();

    // Load meshes
    for( UINT i = 0; i < g_iNumSubDMeshes; i++ )
    {
        // Load the subd mesh from an obj file
        V_RETURN( g_SubDMesh[i].LoadSubDFromObj( g_MeshDesc[i].m_szFileName ) );

        // Much of what follows (conditioning, creating tangents, etc) should be handled offline in the content
        // pipeline.  It's handled here to facilitate easily loading obj files into the sample.

        // Condition the mesh by creating a 1-ring neighborhood around each patch
        if( !g_SubDMesh[i].ConditionMesh() )
            return E_FAIL;

        // Compute a UV tangent frame for the mesh
        g_SubDMesh[i].ComputeTangents();

        // Programmatically add bones and rig the mesh
        g_SubDMesh[i].AddBones( NUM_BONES, g_MeshDesc[i].m_vSpineDir, 1.0f );

        // Sort the patches by size so that we can separate regular and extraordinary patches
        g_SubDMesh[i].SortPatchesBySize();

        // Load any height textures for displacement
        if( g_MeshDesc[i].m_szHeightMap[0] != L'' )
        V_RETURN( g_SubDMesh[i].LoadHeightTexture( pd3dDevice, g_MeshDesc[i].m_szHeightMap ) );

        // Create GPU resources
        V_RETURN( g_SubDMesh[i].CreatePatchesBuffer( pd3dDevice ) );
        V_RETURN( g_SubDMesh[i].CreateBuffersFromSubDMesh( pd3dDevice ) );
    }

    g_pfHeightAmount->SetFloat( g_fHeightAmount );

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Create any D3D10 resources that depend on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnD3D10ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );
    V_RETURN( g_D3DSettingsDlg.OnD3D10ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );

    // Setup the camera's projection parameters
    float fAspectRatio = pBackBufferSurfaceDesc->Width / ( FLOAT )pBackBufferSurfaceDesc->Height;
    g_Camera.SetProjParams( D3DX_PI / 4, fAspectRatio, 0.1f, g_fFarPlane );
    g_Camera.SetWindow( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height );
    g_Camera.SetButtonMasks( MOUSE_MIDDLE_BUTTON, MOUSE_WHEEL, MOUSE_LEFT_BUTTON );

    g_HUD.SetLocation( pBackBufferSurfaceDesc->Width - 170, 0 );
    g_HUD.SetSize( 170, 170 );
    g_SampleUI.SetLocation( pBackBufferSurfaceDesc->Width - 190, pBackBufferSurfaceDesc->Height - 650 );
    g_SampleUI.SetSize( 170, 300 );

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Use the gpu to convert from subds to cubic bezier patches using stream out
//--------------------------------------------------------------------------------------
void ConvertFromSubDToBezier( ID3D10Device* pd3dDevice, CSubDMesh* pMesh )
{
    // Set shader resources
    g_pmConstBoneWorld->SetMatrixArray( ( float* )pMesh->GetTransformMatrices(), 0, pMesh->GetNumTransformMatrices() );
    g_pbufPoints->SetResource( pMesh->GetControlPointSRV() );
    g_pbufBones->SetResource( pMesh->GetControlPointBonesSRV() );

    pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_POINTLIST );

    D3D10_TECHNIQUE_DESC Desc;

    // Do regular and extraordinary patches in separate passes if g_bSeparatePasses is true.
    // Rendering them in separate passes is actually faster than doing it all at once.
    // The reasoning is that regular patches make up the majority of the meshes patches.
    // Because of the fixed valences of regular patches, the regular patch path can be
    // optimized well beyond the extraordinary patch path.  When g_bSeparatePasses is true
    // the regular patches go through the fast path and only the extraordinary patches
    // go through the full (and slow) path.
    int NumExtraPatches = pMesh->GetNumExtraordinaryPatches();
    int NumPasses = 2;
    if( !g_bSeparatePasses )
    {
        NumExtraPatches = pMesh->GetNumPatches();
        NumPasses = 1;
    }

    // Why use arrays of [1] here?  IASetVertexBuffers expects array input.  We could just use define UINT Stride, and use that,
    // but it's good to get into the habit of binding arrays.  It also saves API calls when binding multiple resources.
    UINT stride[1];
    ID3D10Buffer* pBuffers[1];
    UINT zeroOffset[1] = {0};

    // Do up to 2 passes.  The first pass is the extraordinary patches.
    for( int iPassIndex = 0; iPassIndex < NumPasses; iPassIndex++ )
    {
        UINT offset[1];
        offset[0] = iPassIndex * NumExtraPatches * PATCH_STRIDE * sizeof( D3DXVECTOR4 );

        ID3D10EffectTechnique* pTechB = g_pSubDToBezierB;
        ID3D10EffectTechnique* pTechUV = g_pSubDToBezierUV;

        if( g_bRenderSkinned )
        {
            pTechB = g_pSubDToBezierB_Skin;
            pTechUV = g_pSubDToBezierUV_Skin;
        }

        ID3D10InputLayout* pLayout = g_pPatchLayout;
        int NumToDraw = NumExtraPatches;
        stride[0] = sizeof( SUBDPATCH );
        ID3D10Buffer* pBuffers[1];
        pBuffers[0] = pMesh->GetSubDPatchVB();

        // If this is the second pass, then we're doing the regular patches.
        // Setup the techniques appropriately.
        if( iPassIndex == 1 )
        {
            pTechB = g_pSubDToBezierB_Reg;
            pTechUV = g_pSubDToBezierUV_Reg;

            if( g_bRenderSkinned )
            {
                pTechB = g_pSubDToBezierB_Reg_Skin;
                pTechUV = g_pSubDToBezierUV_Reg_Skin;
            }

            NumToDraw = pMesh->GetNumRegularPatches();
            if( NumToDraw == 0 )
                continue;

            pLayout = g_pRegularPatchLayout;
            stride[0] = sizeof( SUBDPATCHREGULAR );
            pBuffers[0] = pMesh->GetSubDPatchRegVB();
        }

        // Set IA data
        pd3dDevice->IASetInputLayout( pLayout );

        // Set patch vb
        pd3dDevice->IASetVertexBuffers( 0, 1, pBuffers, stride, zeroOffset );

        // Stream to BezierB buffer
        pBuffers[0] = pMesh->GetPatchesBufferB();
        pd3dDevice->SOSetTargets( 1, pBuffers, offset );
        pTechB->GetDesc( &Desc );
        for( UINT p = 0; p < Desc.Passes; ++p )
        {
            pTechB->GetPassByIndex( p )->Apply( 0 );
            pd3dDevice->Draw( NumToDraw, 0 );
        }

        // Don't do UV patches for regular patches becuase we don't need the fixup for the normals.
        // We could do UV patches for the regular patches, but it would just waste time.
        if( iPassIndex != 1 )
        {
            if( g_pTechCurrent == g_pTechRenderSceneTan ||
                g_pTechCurrent == g_pTechRenderSceneBumpTan )
            {
                // Stream to BezierUV buffer
                pBuffers[0] = pMesh->GetPatchesBufferUV();
                pd3dDevice->SOSetTargets( 1, pBuffers, offset );
                pTechUV->GetDesc( &Desc );
                for( UINT p = 0; p < Desc.Passes; ++p )
                {
                    pTechUV->GetPassByIndex( p )->Apply( 0 );
                    pd3dDevice->Draw( NumToDraw, 0 );
                }
            }
        }
    }

    // Get back to normal
    pBuffers[0] = NULL;
    pd3dDevice->SOSetTargets( 1, pBuffers, zeroOffset );

}

//--------------------------------------------------------------------------------------
// Helper to get the number of vertices to draw with respect to the subdivison
// amount requested.
//--------------------------------------------------------------------------------------
UINT GetNumVerticesForSubdivisionLevel( UINT iSubDivs )
{
    return ( ( ( iSubDivs + 1 ) * 2 + 2 ) * iSubDivs ) - 2;
}

//--------------------------------------------------------------------------------------
// Render the scene using the D3D10 device
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;

    // If the settings dialog is being shown, then render it instead of rendering the app's scene
    if( g_D3DSettingsDlg.IsActive() )
    {
        g_D3DSettingsDlg.OnRender( fElapsedTime );
        return;
    }

    // Do the conversion from subd to bicubic patches
    if( g_bConvertEveryFrame )
    {
        for( UINT i = 0; i < g_iNumSubDMeshes; i++ )
        {
            ConvertFromSubDToBezier( pd3dDevice, &g_SubDMesh[i] );
        }
    }

    // Clear the render target and depth stencil
    float ClearColor[4] = { 0.2f, 0.2f, 0.4f, 1.0f };
    ID3D10RenderTargetView* pRTV = DXUTGetD3D10RenderTargetView();
    pd3dDevice->ClearRenderTargetView( pRTV, ClearColor );
    ID3D10DepthStencilView* pDSV = DXUTGetD3D10DepthStencilView();
    pd3dDevice->ClearDepthStencilView( pDSV, D3D10_CLEAR_DEPTH, 1.0, 0 );

    D3DXMATRIX mWorldViewProjection;
    D3DXMATRIX mWorld;
    D3DXMATRIX mView;
    D3DXMATRIX mProj;

    D3DXVECTOR3 vEyePt = *g_Camera.GetEyePt();
    D3DXVECTOR3 vDir = *g_Camera.GetLookAtPt() - vEyePt;
    D3DXVECTOR4 vEyeVector( vDir,1 );

    V( g_pfSkinTime->SetFloat( ( float )fTime ) );
    V( g_pfTime->SetFloat( ( float )fTime ) );
    V( g_pvEyeDir->SetFloatVector( ( float* )&vEyeVector ) );

    // Store the old rasterizer state
    ID3D10RasterizerState* pRSState = NULL;
    pd3dDevice->RSGetState( &pRSState );

    for( UINT i = 0; i < g_iNumSubDMeshes; i++ )
    {
        // Get the projection & view matrix from the camera class
        mWorld = *g_Camera.GetWorldMatrix();
        mProj = *g_Camera.GetProjMatrix();
        mView = *g_Camera.GetViewMatrix();

        CSubDMesh* pMesh = &g_SubDMesh[i];

        D3DXMATRIX mTrans;
        D3DXMatrixTranslation( &mTrans, g_MeshDesc[i].m_vPosition.x, g_MeshDesc[i].m_vPosition.y,
                               g_MeshDesc[i].m_vPosition.z );
        D3DXMATRIX mScale;
        D3DXMatrixScaling( &mScale, g_MeshDesc[i].m_vScale.x, g_MeshDesc[i].m_vScale.y, g_MeshDesc[i].m_vScale.z );
        mWorld = mScale * mWorld * mTrans;
        mWorldViewProjection = mWorld * mView * mProj;

        // Set effect variables
        V( g_pmWorldViewProjection->SetMatrix( ( float* )&mWorldViewProjection ) );
        V( g_pmWorld->SetMatrix( ( float* )&mWorld ) );

        // Set all of the buffers needed such as the patches buffers
        V( g_ptxHeight->SetResource( pMesh->GetHeightSRV() ) );
        V( g_pbufPatchesB->SetResource( pMesh->GetPatchesBufferBSRV() ) );
        V( g_pbufPatchesUV->SetResource( pMesh->GetPatchesBufferUVSRV() ) );
        V( g_pbufControlPointsUV->SetResource( pMesh->GetControlPointUVSRV() ) );

        D3DXVECTOR3 vToMesh = vEyePt - g_MeshDesc[i].m_vPosition;
        float DistToCamera = D3DXVec3Length( &vToMesh );
        int iSubDivs = g_iSubdivs;

        // We're attenuating (changing the subdivision level) on a per-object basis if this is enabled.
        // Objects farther away don't need to be subdivided as much as objects closer to the camera.
        if( g_bAttenuateBasedOnDistance )
        {
            const int minSubD = 1;
            const float fNear = 10.0f;

            float fDistAtten = ( DistToCamera - fNear ) / ( g_fFarPlane / 4.0f );

            float fSubDivs = g_iSubdivs + fDistAtten * ( minSubD - g_iSubdivs );

            iSubDivs = ( UINT )ceil( fSubDivs );
            iSubDivs = min( g_iSubdivs, iSubDivs );
            iSubDivs = max( minSubD, iSubDivs );
        }

        // Figure out how many vertices are in the patch
        UINT VerticesToDraw = GetNumVerticesForSubdivisionLevel( iSubDivs );

        // Render the scene with this technique as defined in the .fx file
        ID3D10EffectTechnique* pRenderTechnique = g_pTechCurrent;

        if( pRenderTechnique == g_pTechRenderSceneBump && g_MeshDesc[i].m_szHeightMap[0] == L'' )
        pRenderTechnique = g_pTechRenderScene;
        else if( pRenderTechnique == g_pTechRenderSceneBumpTan && g_MeshDesc[i].m_szHeightMap[0] == L'' )
        pRenderTechnique = g_pTechRenderSceneTan;

        // Render regular and extraordinary patches separately since they will have different shaders
        UINT Strides[1];
        UINT Offsets[1];
        ID3D10Buffer* pVB[1];
        ID3D10Buffer* pIB;

        // Setup the input assembler
        pd3dDevice->IASetInputLayout( g_pPrecalcPatchLayout );
        pVB[0] = g_pPatchVB[iSubDivs - 1];
        pIB = g_pPatchIB[iSubDivs - 1];
        Strides[0] = sizeof( PATCH_VERTEX );
        Offsets[0] = 0;
        pd3dDevice->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );
        pd3dDevice->IASetIndexBuffer( pIB, DXGI_FORMAT_R16_UINT, 0 );
        pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

        // One pass for extraordinary, one for regular
        ID3D10EffectTechnique* pPatchPassTech = pRenderTechnique;
        UINT NumPatchPasses = 2;

        // Unless, of course, we've told it not to do the tangent fixup
        if( pPatchPassTech == g_pTechRenderScene ||
            pPatchPassTech == g_pTechRenderSceneBump )
        {
            NumPatchPasses = 1;
        }

        for( UINT patchpass = 0; patchpass < NumPatchPasses; patchpass++ )
        {
            UINT NumPatches;
            UINT StartInstance;

            if( NumPatchPasses == 1 ) // Just regular patches for everything
            {
                NumPatches = pMesh->GetNumPatches();
                StartInstance = 0;
                pPatchPassTech = pRenderTechnique;
            }
            else if( patchpass == 0 ) // Extraordinary patches
            {
                NumPatches = pMesh->GetNumExtraordinaryPatches();
                StartInstance = 0;
                pPatchPassTech = pRenderTechnique;
            }
            else // regular patches
            {
                NumPatches = pMesh->GetNumRegularPatches();
                StartInstance = pMesh->GetNumExtraordinaryPatches();

                // Regular patches don't require tangent information.  For these,
                // fallback to the Regular patch version of the technique
                if( pRenderTechnique == g_pTechRenderSceneTan )
                    pPatchPassTech = g_pTechRenderScene;
                else if( pRenderTechnique == g_pTechRenderSceneBumpTan )
                    pPatchPassTech = g_pTechRenderSceneBump;
            }

            g_pPatchIDOffset->SetInt( StartInstance );

            //Render
            D3D10_TECHNIQUE_DESC techDesc;
            pPatchPassTech->GetDesc( &techDesc );

            // Add another pass if we're adding a wireframe overlay
            UINT maxpasses = g_bDrawWires ? 2 : 1;
            for( UINT p = 0; p < maxpasses; p++ )
            {
                pPatchPassTech->GetPassByIndex( p )->Apply( 0 );
                pd3dDevice->DrawIndexedInstanced( VerticesToDraw, NumPatches, 0, 0, 0 );

            }
        }
    }

    // Restore the old rasterizer state
    pd3dDevice->RSSetState( pRSState );
    if( pRSState )
        pRSState->Release();

    // Clear out the PS, VS, and GS views so that we don't have any D3DX debug messages about
    // surfaces being bound to both output and input.
    ID3D10ShaderResourceView* pViews[8] = {0,0,0,0,0,0,0,0};
    pd3dDevice->PSSetShaderResources( 0, 8, pViews );
    pd3dDevice->VSSetShaderResources( 0, 8, pViews );
    pd3dDevice->GSSetShaderResources( 0, 8, pViews );

    // Render the HUD
    DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" );
    g_HUD.OnRender( fElapsedTime );
    g_SampleUI.OnRender( fElapsedTime );
    RenderText();
    DXUT_EndPerfEvent();
}

//--------------------------------------------------------------------------------------
// Release D3D10 resources created in OnD3D10ResizedSwapChain
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D10ReleasingSwapChain( void* pUserContext )
{
    g_DialogResourceManager.OnD3D10ReleasingSwapChain();
}

//--------------------------------------------------------------------------------------
// Release D3D10 resources created in OnD3D10CreateDevice
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
{
    g_DialogResourceManager.OnD3D10DestroyDevice();
    g_D3DSettingsDlg.OnD3D10DestroyDevice();
    CDXUTDirectionWidget::StaticOnD3D10DestroyDevice();
    DXUTGetGlobalResourceCache().OnDestroyDevice();
    SAFE_DELETE( g_pTxtHelper );
    SAFE_RELEASE( g_pFont10 );
    SAFE_RELEASE( g_pSprite10 );
    SAFE_RELEASE( g_pEffect10 );
    SAFE_RELEASE( g_pEffectConvert10 );
    SAFE_RELEASE( g_pPatchLayout );
    SAFE_RELEASE( g_pRegularPatchLayout );
    SAFE_RELEASE( g_pPrecalcPatchLayout );

    for( UINT i = 0; i < MAX_DIVS - 1; i++ )
    {
        SAFE_RELEASE( g_pPatchVB[i] );
        SAFE_RELEASE( g_pPatchIB[i] );
    }

    for( UINT i = 0; i < g_iNumSubDMeshes; i++ )
    {
        g_SubDMesh[i].Destroy();
    }
}

//--------------------------------------------------------------------------------------
// Fill the TanM and Ci precalculated tables.  This function precalculates part of the
// stencils (weights) used when calculating UV patches.  We precalculate a lot of the
// values here and just pass them in as shader constants.
//--------------------------------------------------------------------------------------
float g_TanM[MAX_VALENCE][64];
float g_fCi[MAX_VALENCE];
void FillTables()
{
    for( UINT v = 0; v < MAX_VALENCE; v++ )
    {
        int a = 0;
        float CosfPIV = cosf( D3DX_PI / v );
        float VSqrtTerm = ( v * sqrtf( 4.0f + CosfPIV * CosfPIV ) );

        for( UINT i = 0; i < 32; i++ )
        {
            g_TanM[v][i * 2 + a] = ( ( 1.0f / v ) + CosfPIV / VSqrtTerm ) * cosf( ( 2 * D3DX_PI * i ) / ( float )v );
        }
        a = 1;
        for( UINT i = 0; i < 32; i++ )
        {
            g_TanM[v][i * 2 + a] = ( 1.0f / VSqrtTerm ) * cosf( ( 2 * D3DX_PI * i + D3DX_PI ) / ( float )v );
        }
    }

    for( UINT v = 0; v < MAX_VALENCE; v++ )
    {
        g_fCi[v] = cosf( ( 2.0f * D3DX_PI ) / ( v + 3.0f ) );
    }

    g_pTanM->SetFloatArray( ( float* )g_TanM, 0, MAX_VALENCE * 64 );
    g_pfCi->SetFloatArray( g_fCi, 0, MAX_VALENCE );
}

//--------------------------------------------------------------------------------------
// Bernstein basis functions
//--------------------------------------------------------------------------------------
float Basis0( float t )
{
    float invT = 1.0f - t;
    return invT * invT * invT;
}
float Basis1( float t )
{
    float invT = 1.0f - t;
    return 3.0f * t * invT * invT;
}
float Basis2( float t )
{
    float invT = 1.0f - t;
    return 3.0f * t * t * invT;
}
float Basis3( float t )
{
    return t * t * t;
}

//--------------------------------------------------------------------------------------
// Derivative Bernstein basis functions
//--------------------------------------------------------------------------------------
float dBasis0( float t )
{
    float invT = 1.0f - t;
    return -3 * invT * invT;
}
float dBasis1( float t )
{
    float invT = 1.0f - t;
    return 3 * invT * invT - 6 * t * invT;
}
float dBasis2( float t )
{
    float invT = 1.0f - t;
    return 6 * t * invT - 3 * t * t;
}
float dBasis3( float t )
{
    return 3 * t * t;
}

//--------------------------------------------------------------------------------------
// Create an index buffer for a patch of a certain size
//--------------------------------------------------------------------------------------
HRESULT FillIBForPatch( ID3D10Device* pd3dDevice, UINT NumDivisionsPerSide, ID3D10Buffer** ppBuffer )
{
    HRESULT hr = S_OK;

    UINT NumIndices = GetNumVerticesForSubdivisionLevel( NumDivisionsPerSide );
    SHORT* pIndices = new SHORT[NumIndices];
    if( !pIndices )
        return E_OUTOFMEMORY;

    SHORT vIndex = 0;
    UINT iIndex = 0;
    for( UINT v = 0; v < NumDivisionsPerSide; v++ )
    {
        vIndex = ( SHORT )( ( NumDivisionsPerSide + 1 ) * ( NumDivisionsPerSide - v ) );
        for( UINT u = 0; u < NumDivisionsPerSide + 1; u++ )
        {
            pIndices[iIndex] = vIndex;
            iIndex++;
            pIndices[iIndex] = vIndex - ( SHORT )( NumDivisionsPerSide + 1 );
            iIndex++;
            vIndex++;
        }
        if( v != NumDivisionsPerSide - 1 )
        {
            // add a degenerate tri
            pIndices[iIndex] = pIndices[iIndex - 1];
            iIndex++;
            pIndices[iIndex] = ( SHORT )( ( NumDivisionsPerSide + 1 ) * ( NumDivisionsPerSide - ( v + 1 ) ) );
            iIndex++;
        }
    }

    // Create the d3d10 buffer
    D3D10_BUFFER_DESC Desc;
    Desc.ByteWidth = NumIndices * sizeof( SHORT );
    Desc.Usage = D3D10_USAGE_IMMUTABLE;
    Desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
    Desc.CPUAccessFlags = 0;
    Desc.MiscFlags = 0;

    D3D10_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = pIndices;
    InitData.SysMemPitch = 0;
    InitData.SysMemSlicePitch = 0;

    V_RETURN( pd3dDevice->CreateBuffer( &Desc, &InitData, ppBuffer ) );

    SAFE_DELETE_ARRAY( pIndices );
    return hr;
}

//--------------------------------------------------------------------------------------
// Create an vertex buffer for a patch of a certain size.
// This patch will contain precalculated UVs and bernstein polynomial coefficients.
//--------------------------------------------------------------------------------------
HRESULT FillVBForPatch( ID3D10Device* pd3dDevice, UINT NumDivisionsPerSide, ID3D10Buffer** ppBuffer )
{
    HRESULT hr = S_OK;

    UINT NumVertices = ( NumDivisionsPerSide + 1 ) * ( NumDivisionsPerSide + 1 );
    PATCH_VERTEX* pVertices = new PATCH_VERTEX[ NumVertices ];
    if( !pVertices )
        return E_OUTOFMEMORY;

    UINT iVertex = 0;
    float vStart = 0.0f;
    float uDelta = 1.0f / ( float )NumDivisionsPerSide;
    float vDelta = 1.0f / ( float )NumDivisionsPerSide;

    // Loop through terrain vertices and get height from the heightmap
    for( UINT v = 0; v < NumDivisionsPerSide + 1; v++ )
    {
        float uStart = 0.0f;
        for( UINT u = 0; u < NumDivisionsPerSide + 1; u++ )
        {
            D3DXVECTOR2 uv( uStart, vStart );

            pVertices[iVertex].m_UV = uv;

            // Regular basis functions
            pVertices[iVertex].m_BasisU.x = Basis0( uStart );
            pVertices[iVertex].m_BasisU.y = Basis1( uStart );
            pVertices[iVertex].m_BasisU.z = Basis2( uStart );
            pVertices[iVertex].m_BasisU.w = Basis3( uStart );

            pVertices[iVertex].m_BasisV.x = Basis0( vStart );
            pVertices[iVertex].m_BasisV.y = Basis1( vStart );
            pVertices[iVertex].m_BasisV.z = Basis2( vStart );
            pVertices[iVertex].m_BasisV.w = Basis3( vStart );

            // Derivative basis functions
            pVertices[iVertex].m_dBasisU.x = dBasis0( uStart );
            pVertices[iVertex].m_dBasisU.y = dBasis1( uStart );
            pVertices[iVertex].m_dBasisU.z = dBasis2( uStart );
            pVertices[iVertex].m_dBasisU.w = dBasis3( uStart );

            pVertices[iVertex].m_dBasisV.x = dBasis0( vStart );
            pVertices[iVertex].m_dBasisV.y = dBasis1( vStart );
            pVertices[iVertex].m_dBasisV.z = dBasis2( vStart );
            pVertices[iVertex].m_dBasisV.w = dBasis3( vStart );

            iVertex ++;
            uStart += uDelta;
        }
        vStart += vDelta;
    }

    // Create the d3d10 buffer
    D3D10_BUFFER_DESC Desc;
    Desc.ByteWidth = NumVertices * sizeof( PATCH_VERTEX );
    Desc.Usage = D3D10_USAGE_IMMUTABLE;
    Desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    Desc.CPUAccessFlags = 0;
    Desc.MiscFlags = 0;

    D3D10_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = pVertices;
    InitData.SysMemPitch = 0;
    InitData.SysMemSlicePitch = 0;

    V_RETURN( pd3dDevice->CreateBuffer( &Desc, &InitData, ppBuffer ) );

    SAFE_DELETE_ARRAY( pVertices );

    return hr;
}

//--------------------------------------------------------------------------------------
// Create precomputed UV patches for every level of subdivision we're likely to need
//--------------------------------------------------------------------------------------
HRESULT CreatePatchVBsIBs( ID3D10Device* pd3dDevice )
{
    HRESULT hr = S_OK;
    for( UINT i = 1; i < MAX_DIVS; i++ )
    {
        V_RETURN( FillIBForPatch( pd3dDevice, i, &g_pPatchIB[i - 1] ) );
        V_RETURN( FillVBForPatch( pd3dDevice, i, &g_pPatchVB[i - 1] ) );
    }
    return hr;
}