DirectX

Rotação de páginas realista e em tempo real com o DirectX, o C++ e o XAML

Eric Brumer

Baixar o código de exemplo

Durante o desenvolvimento do Windows 8 e Visual Studio 2012, a equipe do Microsoft C++ criado alguns aplicativos de código aberto para mostrar as várias tecnologias de C++ disponíveis para desenvolvedores de software. Um destes apps é o projeto "Austin", um aplicativo de anotações digital escrito em C++, usando DirectX e XAML do Windows Runtime (WinRT).

Neste app, um usuário pode criar um caderno e anotar algumas notas ou diagramas de rabiscar. Há suporte para a adição e exclusão de páginas, cores diferentes de tinta e adicionando os arquivos de imagem de um PC ou de SkyDrive. Figura 1 mostra alguns screenshots do aplicativo em ação.

Project “Austin”
Figura 1 projeto "Austin"

Os usuários podem exibir seus notebooks de três maneiras: uma única linha de páginas (como em Figura 1), uma grade de páginas ou como se as páginas estavam empilhadas uns sobre os outros. Nesta vista empilhados, o usuário pode folhear páginas swiping seu dedo através da página, como se ele fosse folhear páginas em um livro de verdade. As páginas digitais são enroladas em tempo real, com base na posição do dedo do usuário, como ele vira a página. Figura 2 mostra a página de ondulação em ação.

Page Curling
Figura 2 página de ondulação

O recurso de página-curling também manipula uncurling página. Quando o usuário deixa para ir de uma página enquanto curling, a página age como um pedaço de papel: se a página estiver abaixo de um certo limite, é uncurls para uma posição de postura bemol; se a página estiver acima do limite, isso uncurls mas acabamentos girando.

Este artigo descreve detalhadamente a geometria, tecnologias e o código usado para executar a página em tempo real de ondulação e uncurling.

A geometria da ondulação de página

Antes de explorar o design geral, obtenho a geometria e a matemática fora do caminho. Esta informação é tomada em grande parte do meu post de blog do MSDN, "projeto Austin parte 2 de 6: Curling Page"(bit.ly/THF40f).

O papel de 2006, "Virando páginas de livros eletrônicos 3D" (L. Hong, S.K. Cartão e J. Chen), descreve como ondulação de página pode ser simulada por deformar o papel em torno de um cone imaginário, como mostrado na Figura 3. Alterando o formato e a posição do cone, você pode simular curling mais (ou menos).

Flat Paper (Black) Curling Around a Cone (Green) to Become the Curled Paper (Red)
Figura 3 Flat de papel (preto) Curling em torno de um Cone (verde) para se tornar o papel enrolado (vermelho)

Da mesma forma, a ondulação de página também pode ser simulada por deformar o papel em torno de um cilindro imaginário, como mostrado na Figura 4.

Flat Paper (Black) Curling Around a Cylinder (Green) to Become the Curled Paper (Red)
Figura 4 televisões de papel Curling (preto) ao redor de um cilindro (verde) para se tornar o papel enrolado (vermelho)

Meu método de ondulação de página é a seguinte:

  • Se o usuário está ondulando do canto superior direito da página, deforme a página em torno de um cone com o ângulo θ e apex coordenadas (0, Ay, 0).
  • Se o usuário é ondulação da centro-direita da página, deforme a página ao redor de um cilindro com raio r.
  • Se o usuário está ondulando do canto inferior-direito da página, deforme a página em torno de um cone invertido.
  • Se o usuário está ondulando em qualquer lugar no meio, deforme a página em torno da combinação linear de um cone e um cilindro, com base na coordenada y da entrada.
  • Após a deformação, gire o papel ao redor do eixo y.

Aqui estão os detalhes para transformar uma página em torno de um cilindro. (O artigo Hong descreve geometria similar para transformar uma página em torno de um cone). Dada a Pflat ponto com coordenadas {X1, y1, z1 = 0} de uma página simples, o objetivo é transformá-lo em Pcurl com coordenadas {X2, y2, z2}, o ponto em um cilindro com raio r que está mentindo sobre a "coluna vertebral" do livro. Agora, dê uma olhada Figura 5, que mostra a extremidade do cilindro. Você pode ver o x e z eixos (os funcionamentos de eixo y dentro e fora da página). Observe que estou representando o papel liso e o cilindro usando as mesmas cores como as figuras anteriores.

Transforming Pflat to Pcurl
Figura 5-transformação de Pflat para Pcurl

O insight fundamental é que a distância entre a origem para Pflat (1) é o mesmo que a distância de arco desde a origem até Pcurl ao longo do cilindro. Então, de geometria simples, eu posso dizer que o ângulo β = 1 x / r. Agora, para obter o Pcurl, tomar a origem, movê-la para baixo, r no eixo z, giram em torno de β e, em seguida, subir por r no eixo z. O método de curlPage em Figura 6 mostra o código para deformar o buffer de vértice para uma página. O buffer de vértices e informações de coordenadas de página são abstraídos fora.

Figura 6 deformando o Buffer de vértice

void page_curl::curlPage(curl_parameters curlParams)
{
  float theta = curlParams.theta;
  float Ay = curlParams.ay;
  float alpha = curlParams.alpha;
  float conicContribution = curlParams.conicContribution;
  // As the user grabs toward the middle-right of the page, curl the
  // paper by deforming it on to a cylinder.
The cylinder radius is taken
  // as the endpoint of the cone parameters: for example,
  // cylRadius = R*sin(theta) distance to where R is the the rightmost
  // point on the page, all the way up.
float cylR = sqrt(  _vertexCountX * _vertexCountX
                    + (_vertexCountY /2 - Ay)*( _vertexCountY /2 - Ay));
  float cylRadius = cylR * sin(theta);
  // Flipping from top corner or bottom corner?
float posNegOne;
  if (conicContribution > 0)
  {
    // Top corner
    posNegOne = 1.0f;
  }
  else
  {
    // Bottom corner
    posNegOne = -1.0f;
    Ay = -Ay + _vertexCountY;
  }
  conicContribution = abs(conicContribution);
  for (int j = 0; j < _vertexCountY; j++)
  {
    for (int i = 0; i < _vertexCountX; i++)
    {
      float x = (float)i;
      float y = (float)j;
      float z = 0;
      float coneX = x;
      float coneY = y;
      float coneZ = z;
      {
        // Compute conical parameters and deform
        float R = sqrt(x * x + (y - Ay)*(y - Ay));
        float r = R * sin(theta);
        float beta  = asin(x / R) / sin(theta);
        coneX = r * sin(beta);
        coneY = R + posNegOne * Ay - r * (1 - cos(beta)) * sin(theta);
        coneZ = r * (1 - cos(beta)) * cos(theta);
        // Then rotate by alpha about the y axis
        coneX = coneX * cos(alpha) - coneZ * sin(alpha);
        coneZ = coneX * sin(alpha) + coneZ * cos(alpha);
      }
      float cylX = x;
      float cylY = y;
      float cylZ = z;
      {
        float beta = cylX / cylRadius;
        // Rotate (0,0,0) by beta around line given by x = 0, z = cylRadius
        // aka Rotate (0,0,-cylRadius) by beta, then add cylRadius back
        // to z coordinate
        cylZ = -cylRadius;
        cylX = -cylZ * sin(beta);
        cylZ = cylZ * cos(beta);
        cylZ += cylRadius;
        // Then rotate by alpha about the y axis
        cylX = cylX * cos(alpha) - cylZ * sin(alpha);
        cylZ = cylX * sin(alpha) + cylZ * cos(alpha);
      }
      // Combine cone & cylinder results
      x = conicContribution * coneX + (1-conicContribution) * cylX;
      y = conicContribution * coneY + (1-conicContribution) * cylY;
      z = conicContribution * coneZ + (1-conicContribution) * cylZ;
      _vertexBuffer[j * _vertexCountX + i].position.x = x;
      _vertexBuffer[j * _vertexCountX + i].position.y = y;
      _vertexBuffer[j * _vertexCountX + i].position.z = z;
    }
  }
}

A variável conicContribution, variando de -1 a + 1, captura a posição no eixo y o usuário tocou. Um valor -1 representa o usuário tocar no fundo da página, e + 1 representa o topo da página.

O conjunto completo de parâmetros de deformação é capturado em curl_parameters:

struct curl_parameters
{
  curl_parameters() {}
  curl_parameters(float t, float a, float ang, float c) :
    theta(t), ay(a), angle(ang), conicContribution(c) {}
  float theta;  // Angle of right-cone
  float ay;     // Location on y axis of cone apex
  float alpha;  // Rotation about y axis
  float conicContribution;  // South tip cone == -1, cylinder == 0,
    north tip cone == 1
};

Observe que o raio do cilindro está faltando esta struct; Eu estou tomando um atalho pela computação com base em parâmetros do cone, como em Figura 6.

Arquitetura

Com a geometria do caminho, pode se concentrar no design e arquitetura de página-curling. O objetivo do projeto é para permitir a página realista de ondulação e uncurling sem perder a fluidez. Por exemplo, o usuário deve ser capaz de parcialmente enrolar uma página, deixe ir para a página uncurls um pouco, então continue enrolando a página, permanecendo a animação fluida e realista.

A arquitetura de curling página projeto Austin é implementada na classe page_curl, mostrado na Figura 7.

Figura 7 o page_curl classe

class page_curl
{
public:
  void attachPage(const std::shared_ptr<paper_sheet_node> &pageNode);
  void startUserCurl(float x, float y);
  void startAutoCurl();
  void onRender();
private:
  struct curl_parameters
  {
    curl_parameters() {}
    curl_parameters(float t, float a, float ang, float c) :
      theta(t), ay(a), angle(ang), conicContribution(c) {}
    float theta;  // Angle of right cone
    float ay;     // Location on y axis of cone apex
    float alpha;  // Rotation about y axis
    float conicContribution;  
    // South tip cone == -1, cylinder == 0, north tip cone == 1
  };
  void continueAutoCurl();
  page_curl::curl_parameters computeCurlParameters(float x, float y);
  void page_curl::curlPage(page_curl::curl_parameters curlParams);
  ...
Other helpers that will be discussed later ...
std::shared_ptr<paper_sheet_node> _pageNode; // Page abstraction
  bool _userCurl;           // True if the user is curling the page
  bool _autoCurl;           // True if the page is uncurling
  float _autoCurlStartTime; // The time the user let go to start uncurling
  // Allows for smooth animations
  curl_parameters _currentCone;
  curl_parameters _nextCone;
};

Aqui estão os métodos que importam:

void page_curl::attachPage (const std::shared_ptr <paper_sheet_node> & pageNode) é chamado pelo código do projeto Austin sempre que uma página é enrolada. A estrutura de dados paper_sheet_node captura todas as informações pertinentes sobre o sistema de coordenadas da página, bem como o buffer de vértice DirectX usado para processar esta página em particular. A execução não é discutida neste artigo.

void page_curl::startUserCurl (float x, float y) é chamado por Austin o projeto código de manipulador de eventos de entrada do usuário, para indicar que o usuário tem pressionado o dedo e está ondulando na posição (x, y). Este código faz o seguinte:

  • define o bit de estado _userCurl para indicar que o usuário está ondulando a página
  • Desconfigura o bit de estado _autoCurl para parar qualquer uncurling se está em andamento
  • define o _nextCurlParams para os parâmetros de deformação, com base na posição do usuário (x, y)

void page_curl::startAutoCurl é chamado pelo projeto Austin usuário entrado manipulador para indicar que o usuário tem larga da tela. Este código faz o seguinte:

  • Desconfigura o bit de estado _userCurl para indicar que o usuário já não é a página de ondulação
  • define o bit de estado _autoCurl para indicar um uncurl está em andamento, com um carimbo de hora de quando começou a uncurl

void page_curl::onRender é chamado pelo loop de processamento de Austin de projeto para cada quadro. Note que esta é a única função que na verdade deforma o buffer de vértice. Esse código funciona da seguinte maneira:

  • Se _userCurl ou _autoCurl for definida, o código deforma o buffer de vértice para os parâmetros calculados a partir de _nextCurlParams e _currentCurlParams. Usar ambos garante uma ondulação suave, conforme discutido posteriormente neste artigo.
  • Se _autoCurl for definida, o código chama o continueAutoCurl
  • Define o _currentCurlParams para _nextCurlParams

void page_curl::continueAutoCurl é chamado pelo page_curl::onRender se a página é uncurling. Este código:

  • calcula _nextCurlParams com base em quando o uncurl começou
  • Desconfigura o _autoCurl se a página foi concluída curling

page_curl::curl_parameters page_curl::computeCurlParameters (float x, float y) calcula os parâmetros de onda (teta, Ay, alfa, conicContribution) com base na entrada do usuário.

Agora que você já viu a arquitetura geral, eu vou encher em cada um dos métodos públicos e privados. Eu escolhi o projeto total para tornar mais fácil para garantir animação suave. A chave é separar startUserCurl e onRender e manter o estado entre os dois.

Agora discutiremos alguns desses métodos, proporcionando motivação ou plano de fundo sobre as decisões de design.

Animação suave

Tendo em conta as funções descritas anteriormente, pode parecer adequado para startUserCurl para simplesmente ler a posição do dedo do usuário e para onRender deformar-se simplesmente a página para esses parâmetros.

Infelizmente, esta animação particular pode olhar feia se o usuário move o dedo muito rápido. Se onRender desenha a página deformada em 60 frames por segundo (fps), é possível que entre dois quadros o usuário moveu o dedo no meio do caminho através da tela. Sobre um frame, a página está deformada para um estado quase plano. Se, no quadro seguinte, a página é deformada para um estado totalmente enrolado, a fluidez da animação é perdida e... Bem, parece feio.

Para contornar esse problema, mantenho o controle de não somente _nextCurlParams (o local desejado, onde a onda deve ir baseado em entrada do usuário ou as fórmulas para uncurling), mas também o estado atual da ondulação em _currentCurlParams. Se o local onda desejada é muito longe do local onda existente, então eu deve preferivelmente ser ondulação para um valor intermediário que garante a animação é suave.

O termo "muito longe" está aberto a interpretação. Porque há quatro elementos na estrutura de cone_parameters, e cada um é um número de ponto flutuante, eu trato _currentCurlParams e _nextCurlParams como pontos no espaço tetradimensional. A distância entre os parâmetros de dois onda então é apenas a distância entre dois pontos.

Da mesma forma, o termo "valor intermediário" também está aberto a interpretação. Se o _nextCurlParams está muito longe de _currentCurlParams, eu escolho um ponto intermédio que está mais perto _nextCurlParams proporcional à distância entre dois pontos. Então, se o usuário começa com uma página plana e cachos é extremamente rápida, a página é exibida inicialmente a Primavera para a frente rapidamente, mas em seguida desacelera o mais perto fica para o local desejado. Porque isso acontece a 60 fps, o efeito global é muito menor, mas do ponto de vista de usabilidade os resultados ótimos.

Figura 8 mostra o código completo de renderização.

Figura 8 o código de processamento

void page_curl::onRender()
{
  // Read state under a lock
  curl_parameters nextCurlParams;
  curl_parameters currentCurlParams;
  bool userCurl;
  bool autoCurl;
  LOCK(_mutex)
  {
    nextCurlParams = _nextCurlParams;
    currentCurlParams = _currentCurlParams;
    userCurl = _userCurl;
    autoCurl = _autoCurl;
  }
  // Smooth going from currentCurlParams to nextCurlParams
  curl_parameters curl;
  float dt = nextCurlParams.theta - currentCurlParams.theta;
  float da = nextCurlParams.ay    - currentCurlParams.ay;
  float dr = nextCurlParams.alpha - currentCurlParams.alpha;
  float dc = nextCurlParams.conicContribution -
    currentCurlParams.conicContribution;
  float distance = sqrt(dt * dt + da * da + dr * dr + dc * dc);
  if (distance < constants::general::maxCurlDistance())
  {
    curl = nextCurlParams;
  }
  else
  {
    float scale = maxDistance / distance;
    curl.theta = currentCurlParams.theta + scale * dt;
    curl.ay =  currentCurlParams.ay  + scale * da;
    curl.alpha = currentCurlParams.alpha + scale * dr;
    curl.conicContribution = 
      currentCurlParams.conicContribution + scale * dc;
  }
  // Deform the vertex buffer
  if (userCurl || autoCurl)
  {
    LOCK(_mutex)
    {
      _currentCurlParams = curl;
    }
    this->curlPage(curl);
  }
  // Continue (or stop) uncurling
  if (autoCurl)
  {
    this->continueAutoCurl();
  }
}

Manipulação de entrada do usuário (e a falta dela)

Projeto Austin, sendo um aplicativo C++ DirectX XAML, faz uso das APIs do WinRT. Reconhecimento de gestos é tratado pelo sistema operacional — especificamente pelo Windows::UI::Input::GestureRecognizer.

Curtir o evento onManipulationUpdated para chamar startUserCurl (x, y) quando o usuário está ondulando a página é simples. O código para startUserCurl é então:

// x is scaled to (0, 1) and y is scaled to (-1, 1)
void page_curl::startUserCurl(float x, float y)
{
  curl_parameters curl = this->computeCurlParameters(x, y);
  LOCK(_mutex)
  {
    // Set curl state, to be consumed by onRender()
    _nextCurlParams = curl;
    _userCurl = true;
    _autoCurl = false;
  }
}

Da mesma forma, é simples de ligar o evento onManipulationCompleted para chamar startAutoCurl quando o usuário deixa para ir da página. Figura 9 mostra o código para startAutoCurl.

Figura 9 o startAutoCurl método.

void page_curl::startAutoCurl()
{
  LOCK(_mutex)
  {
    // It's possible the user let go, but the page is
    // already fully curled or uncurled
    bool shouldAutoCurl = !this->doneAutoCurling(curl);
    _userCurl = false;
    _autoCurl = shouldAutoCurl;
    if (shouldAutoCurl)
    {
      _autoCurlStartTime = constants::general::currentTime();
    }
  }
}

O código mais interessante é para tratamento de auto-uncurling quando o usuário deixa para ir da página; a página continuará para crianças até que é completamente plana ou tem completamente curvado para a frente. Baseio esta transformação sobre os parâmetros de onda atual e o tempo decorrido (quadrado), desde que começou a uncurling. Desta forma, a página começa a uncurl lentamente, mas como o tempo passa ele uncurls mais rápido e mais rápido. Esta é uma maneira barata para tentar simular a gravidade. A Figura 10 mostra o código.

Figura 10 manipulação Auto-Uncurling

void page_curl::continueAutoCurl()
{
  LOCK(_mutex)
  {
    if (this->doneAutoCurling(curl))
    {
      _autoCurl = false;
    }
    else
    {
      float time = constants::general::currentTime() - 
        _autoCurlStartTime;
      _nextCurlParams = this->nextAutoCurlParams(
        _currentCurlParams, 
        time * time);
    }
  }
}

Ajuste o Curling para o realismo

Ausentes do anterior seções é o código para computeCurl­parâmetros, doneAutoCurling e nextAutoCurlParams. Estas são funções ajustáveis que envolvem constantes, fórmulas e heurísticas que são o resultado de horas de meticuloso de tentativa e erro.

Por exemplo, passei muitas horas a tentar alcançar resultados razoáveis para computeCurlParameters. Figura 11 mostra duas versões do código — que simula a ondulação de um pedaço grosso de papel (as páginas dentro de um livro) e um segundo que simula uma tampa de plástico (a capa de um livro de capa mole, por exemplo).

Figura 11 Curling nos dois tipos de páginas

// Helper macro for a straight line F(x) that passes through {x1, y1} and {x2, y2}.
// This can't be a template function (C++ doesn't let you have float literals
// as template parameters).
#define STRAIGHT_LINE(x1, y1, x2, y2, x) 
    (((y2 - y1) / (x2 - x1)) * (x - x1) + y1)
page_curl::curl_parameters page_curl::paperParams(float x, float y)
{
  float theta, ay, alpha;
  if (x > 0.95f)
  {
    theta = STRAIGHT_LINE(1.0f,  90.0f, 0.95f, 60.0f, x);
    ay    = STRAIGHT_LINE(1.0f, -20.0f, 0.95f, -5.0f, x);
    alpha = 0.0;
  }
  else if (x > 0.8333f)
  {
    theta = STRAIGHT_LINE(0.95f,  60.0f, 0.8333f, 55.0f, x);
    ay    = STRAIGHT_LINE(0.95f, -5.0f,  0.8333f, -4.0f, x);
    alpha = STRAIGHT_LINE(0.95f,  0.0f,  0.8333f, 13.0f, x);
  }
  else if (x > 0.3333f)
  {
    theta = STRAIGHT_LINE(0.8333f, 55.0f, 0.3333f,  45.0f, x);
    ay    = STRAIGHT_LINE(0.8333f, -4.0f, 0.3333f, -10.0f, x);
    alpha = STRAIGHT_LINE(0.8333f, 13.0f, 0.3333f,  35.0f, x);
  }
  else if (x > 0.1666f)
  {
    theta = STRAIGHT_LINE(0.3333f,  45.0f, 0.1666f,  25.0f, x);
    ay    = STRAIGHT_LINE(0.3333f, -10.0f, 0.1666f, -30.0f, x);
    alpha = STRAIGHT_LINE(0.3333f,  35.0f, 0.1666f,  60.0f, x);
  }
  else
  {
    theta = STRAIGHT_LINE(0.1666f,  25.0f, 0.0f,  20.0f, x);
    ay    = STRAIGHT_LINE(0.1666f, -30.0f, 0.0f, -40.0f, x);
    alpha = STRAIGHT_LINE(0.1666f,  60.0f, 0.0f,  95.0f, x);
  }
  page_curl::curl_parameters cp(theta, ay, alpha, y);
  return cp;
}
page_curl::curl_parameters page_curl::plasticParams(float x, float y)
{
  float theta, ay, alpha;
  if (x > 0.95f)
  {
    theta = STRAIGHT_LINE(1.0f,  90.0f, 0.9f,  40.0f, x);
    ay    = STRAIGHT_LINE(1.0f, -30.0f, 0.9f, -20.0f, x);
    alpha = 0.0;
  }
  else
  {
    theta = STRAIGHT_LINE(0.95f,  40.0f, 0.0f,  35.0f, x);
    ay    = STRAIGHT_LINE(0.95f, -20.0f, 0.0f, -25.0f, x);
    alpha = STRAIGHT_LINE(0.95f,   0.0f, 0.0f,  95.0f, x);
  }
  page_curl::curl_parameters cp(theta, ay, angle, y);
  return cp;
}

O código para saber quando a ondulação está completa, apenas envolve verificar se a página é completamente plana, ou é completamente enrolada:

bool page_curl::doneAutoCurling(curl_parameters curl)
{
  bool doneCurlBackwards =  (curl.theta > 89.999f)
                         && (curl.ay < -69.999f)
                         && (curl.alpha < 0.001f)
                         && (abs(curl.conicContribution) > 0.999f);
  bool doneCurlForwards = (curl.alpha > 99.999f);
  return doneCurlBackwards || doneCurlForwards;
}

E a última, minha versão do auto-curl, mostrado no Figura 12, baseia-se na posição atual da onda e a Praça do tempo decorrido desde que começou a onda. Em vez de uncurling uma página da mesma forma que é enrolado, simplesmente tem os parâmetros de onda linearmente aproximar os parâmetros para uma página simples, mas deixe a página cair para trás (se o usuário deixar ir quando a página foi apenas ligeiramente ondulada) ou para a frente (se o usuário deixar ir quando a página foi enrolada em sua maioria). Usando esta técnica e a Praça do tempo decorrido, a página tem um belo salto para quando você deixar ir. Eu realmente gosto de como ele se parece.

Figura 12 a minha versão do Auto-Curling

page_curl::curl_parameters page_curl::nextAutoCurlParams(
  curl_parameters curl, float time)
{
  curl_parameters nextCurl;
  if (curl.alpha > 40)
  {
    nextCurl.theta = min(curl.theta + time/800000.0f,  50.0f);
    nextCurl.ay    = curl.ay;
    nextCurl.alpha = min(curl.alpha + time/200000.0f, 100.0f);
  }
  else
  {
    nextCurl.theta = min(curl.theta + time/100000.0f, 90.0f);
    nextCurl.ay    = max(curl.ay - time/200000.0f,   -70.0f);
    nextCurl.alpha = max(curl.alpha - time/300000.0f,  0.0f);
  }
  if (curl.conicContribution > 0.0)
  {
    nextCurl.conicContribution =
      min(curl.conicContribution + time/100000.0f, 1.0f);
  }
  else
  {
    nextCurl.conicContribution =
      max(curl.conicContribution - time/100000.0f, -1.0f);
  }
  return nextCurl;
}

Uma coisa que eu desejo que eu tinha implementado é inércia página quando uncurling. Os usuários devem ser capazes de arremessar a página. Quando eles deixar ir, a página deve continuar ondulando na mesma direção que foi arremessado, até as forças de arraste para parar e leigos volta plana. Isto poderia ser implementado adicionando história para onRender, acompanhando as posições de alguns últimas do dedo do usuário, e fazendo uso do presente nas fórmulas em nextAutoCurlParams.

Desempenho

O método curlPage tem que fazer um pouco de matemática para enrolar uma única página. Pelas minhas contas, há nove chamadas para a função sin, oito para cos, uma de arcsin, para sqrt, e cerca de duas dúzias de multiplica, além de adições e subtrações — para cada vértice no modelo de papel — para cada quadro que está sendo processado!

Austin projeto visa para 60 fps; assim, cada frame de processamento pode tomar não mais de 15 ms, para que o app sensação lenta.

O desempenho necessário é alcançada, garantindo que é vetorizado do loop mais interno, onde o Visual Studio C++ compilador gera instruções Streaming SIMD Extensions 2 (SSE2) para tirar proveito das unidades de vetor de CPU. O compilador é capaz de vetorizar todas as funções transcendentais no Math. h arquivo de cabeçalho.

Neste caso, o loop interno calcula a posição enrolada para quatro vértices de cada vez. O aumento de desempenho libera a CPU para outras tarefas de processamento, tais como aplicação de sombras para a página enrolada.

Leia mais sobre a auto-vectorizer no MSDN e sobre a programação paralela no blog código nativo (bit.ly/bWfC5Y).

Conclusão

Eu gostaria de agradecer o grande povo que trabalhou no projeto de Austin, em particular, Jorge Pereira, George Mileka e Alan Chan. Quando que comecei a trabalhar no projeto já tinham um grande app, e sinto-me afortunado por ter passado algum tempo, acrescentando-lhe um pouco realismo. Ele me ajudou a entender a beleza na simplicidade, e quão difícil pode ser para fazer coisas simples!

Você pode encontrar mais informações sobre o app, incluindo alguns vídeos, sobre os blogs do MSDN procurando por projeto Austin. E você encontrará o código no austin.codeplex.com.

Eric Brumer é engenheiro de desenvolvimento de software da Microsoft, trabalhando na equipe do Visual C++ compilador otimizador. Contactá-lo no ericbr@microsoft.com.

Graças ao especialista técnico seguir para revisar este artigo: George milekUm (Microsoft)
George Mileka é engenheiro de desenvolvimento de software da Microsoft, trabalhando na equipe da Visual C++ bibliotecas. Contactá-lo no gmileka@microsoft.com.