flowchart LR
subgraph POR["por vértice"]
P["posição<br/>onde o ponto está"]
N["normal<br/>como a luz responde ali"]
U["coordenada de textura<br/>que pedaço da imagem cai ali"]
end
F["faces<br/>que vértices formam cada triângulo"]
POR --> M["a peça desenhada"]
F --> M
P -.->|"errada"| S1["silhueta MUDA<br/>o defeito se enxerga"]
N -.->|"errada"| S2["silhueta intacta<br/>a peça escurece"]
U -.->|"errada"| S3["silhueta intacta<br/>o desenho estica"]
F -.->|"invertida"| S4["silhueta intacta<br/>buraco visível de um ângulo só"]
1 Técnicas de modelagem de ambientes virtuais — Resumo
Versão de revisão. As seis definições deste capítulo estão aqui inteiras, e as ressalvas junto — são elas que se erram quando se revisa às pressas. O que encolheu foi o número de exemplos. O tratamento longo está na versão completa deste capítulo e no capítulo correspondente do livro.
Abra a oficina do capítulo anterior e olhe para a engrenagem grande. Catorze dentes, flanco chapado, vale fundo. A peça ocupa a tela de um jeito que a caixa cinzenta não ocupava, e dá para ver que alguém trabalhou ali.
Agora olhe em volta dela. Não há nada para ver. A engrenagem carrega uma caixa invisível de 19,5 por 5,6 por 19,6 centímetros, com dois centímetros de sobra em cada direção. Essa caixa levou mais tempo para ser decidida do que os catorze dentes. Guarde as duas imagens, porque elas voltam no fim.
O senso comum diz que modelar um ambiente é fazer as coisas parecerem certas. É metade do trabalho, e é a metade que se confere olhando. A outra metade decide o que acontece quando alguém encosta. Ela não tem imagem. Confere-se de outro jeito.
1.1 Quatro listas, e três erram em silêncio
Três dos quatro atributos de uma peça erram sem mudar um pixel da silhueta.
Uma malha é um feixe de listas paralelas, com uma entrada por vértice. Cada lista responde a uma pergunta diferente sobre o mesmo ponto. Onde ele está, como a luz o encontra, que pedaço de imagem cai sobre ele. As três perguntas não têm relação umas com as outras. Responder bem a uma não ajuda nas outras duas.
Uma malha é um conjunto de listas paralelas, com uma entrada por vértice, mais uma lista de faces que liga esses vértices.
Por vértice guardam-se três coisas: a posição, que decide onde o ponto está no espaço; a normal, que decide como a luz responde ali; e o par de coordenadas de textura, que decide qual pedaço de uma imagem cai sobre aquele ponto. A lista de faces enumera triângulos por índice de vértice.
A forma que se vê é o resultado das quatro listas juntas, e o sintoma de erro é desigual entre elas. Errar as posições muda a silhueta, e isso se enxerga. Errar qualquer uma das outras três conserva a silhueta exata e muda o que aparece dentro dela.
O menor exemplo concreto é uma chamada que registra um vértice. Ela recebe oito números: três de posição, três de direção e dois de coordenada de imagem. Nenhum se deduz dos outros, e quem chama precisa ter opinião sobre os oito, um por um, no instante em que o ponto nasce.
Pois é: a caixa pedida pronta à biblioteca dispensa as oito opiniões e devolve uma caixa correta. Por dispensá-las, ela não ensina a diagnosticar nada. Guarde também o que a definição não exige. Triângulos de tamanhos parecidos, vértices compartilhados entre faces vizinhas, coordenadas de imagem cobrindo a superfície sem sobreposição. A engrenagem grande viola as três, e desenha certo.
Alguém escreve a casca lateral de uma engrenagem e a peça sai escura. Não preta, não sumida: escura, sem leitura, com o contorno dos catorze dentes recortado com perfeição. O primeiro palpite é a iluminação, e a tarde vai embora em ajustes de intensidade, de posição e de cor. O defeito estava na direção que cada vértice declara como “para fora”. Ela saiu de dois fatores em ordem trocada, e aponta para dentro da peça. A silhueta ficou perfeita a tarde inteira, porque a direção que decide a luz não participa do recorte contra o fundo.
A orientação de uma face é o sentido em que ela enumera os seus vértices. A biblioteca desenha a face cuja borda percorre o sentido anti-horário quando vista de fora, e omite a face percorrida no sentido contrário — sem erro, aviso ou mensagem.
A normal de um vértice é a direção que o programa declara como “para fora” naquele ponto, e é ela que a iluminação consulta para decidir o brilho da superfície.
Um vértice usado por várias faces carrega uma normal só, que serve a todas elas. Compartilhar o ponto entre faces de orientações diferentes suaviza o canto entre elas; duplicar o ponto, com uma cópia por face, conserva a aresta viva ao preço de mais vértices na malha.
As duas erram de maneiras parecidas na descrição e opostas no efeito. A orientação errada produz ausência: a face deixa de ser desenhada, e por ali se vê o interior da peça. Como a face oposta continua sendo desenhada, o buraco aparece de um lado e não do outro. A engrenagem some pela metade quando a câmera passa por cima, e volta ao normal quando ela desce. O defeito parece intermitente.
A normal errada produz escuridão, com a face inteira no lugar. Que sinal separa esse caso de um problema de iluminação? Gire a peça devagar contra a fonte e observe a lateral. A normal invertida escurece justamente quando a face encara a luz, que é o contrário do que qualquer superfície faz. Dez segundos de teste apagam a tarde descrita ali em cima.
a normal da casca lateral, em src/bancada/content/malha.ts
// Normal para fora: o produto vetorial do eixo vertical pela direção da
// aresta. Na ordem inversa ela apontaria para dentro da peça, e a luz
// acertaria a face pelo lado errado — a peça fica escura e ninguém acha o
// motivo, porque a silhueta continua perfeita.
const nx: number = (q.z - p.z) / comprimento;
const nz: number = -(q.x - p.x) / comprimento;
Duas linhas de conta e quatro de comentário. A desproporção é o ponto. Na versão certa e na errada as duas linhas ficam idênticas, a menos da ordem de dois fatores. Nada no código torna uma delas mais plausível que a outra. O comentário é o que sobrevive ao esquecimento de quem escreveu. Ele diz qual dos dois fatores vem primeiro, e por quê.
Por que uma malha gastaria vértices a mais de propósito? A engrenagem grande sai com 338 vértices para 224 triângulos, quando compartilhar pontos (o instinto de todo mundo) devolveria bem menos. O que se compra é a aresta viva. Um vértice compartilhado por duas faces carrega uma normal só, alguma média entre as duas orientações. A luz atravessa o canto, e o canto deixa de existir para o olho. Numa esfera é exatamente o que se quer, porque ali o canto é artefato da discretização.
Num dente de engrenagem acontece o oposto. O flanco encontra a crista num ângulo que existe na peça de verdade, e arredondá-lo transforma peça mecânica em brinquedo. A conta dos 338 fecha à mão: 57 na face de cima, 57 na de baixo, e 224 na casca lateral, quatro por aresta e nenhum compartilhado. O eixo percorre o mesmo caminho e sai com 74 vértices para 48 triângulos.
Antes de seguir. Pegue um objeto do seu projeto e olhe cada canto dele. Esse ângulo existe no objeto de verdade, ou é resíduo de você ter aproximado uma curva por pedaços retos? A resposta decide se o vértice ali se compartilha ou se duplica, e ela não depende de uma linha de código.
1.2 Catorze grupos de quatro pontos, e nada mais
Uma engrenagem é um contorno plano puxado para cima. A frase parece simplificação didática e é a arquitetura literal do arquivo que constrói a peça. O contorno de um dente tem quatro pontos, e cada um é uma decisão: sobe à crista, atravessa a crista, desce à raiz, atravessa o vale. Os ângulos em que isso acontece separam um dente que parece dente de um serrilhado qualquer.
Geometria paramétrica é a forma descrita por uma função dos seus parâmetros e construída pela execução dessa função.
A construção por perfil e extrusão tem duas partes de papéis desiguais. O perfil é um contorno plano, dado como sequência de pontos, e é nele que mora toda a decisão de forma. A extrusão levanta o contorno numa espessura, fecha as duas tampas e costura a casca lateral, e é a mesma rotina para qualquer perfil.
Trocar o perfil troca a peça. Os parâmetros do perfil — quantos elementos, com que raios — são o botão de densidade da malha: cada elemento acrescenta um número fixo de triângulos ao resultado, e esse número é pago em todo quadro.
A engrenagem grande tem catorze dentes, raio de raiz de sessenta e oito milímetros e raio de crista de setenta e oito. O contorno resultante tem 56 pontos, quatro por dente. A rotina de extrusão recebe esses 56 pontos e devolve a peça inteira, sem saber que aquilo era uma engrenagem. A definição não crava número algum, e não exige perfil fechado, espessura constante nem casca perpendicular às tampas.
A desigualdade entre as duas partes é de onde vem a economia toda. O perfil é curto, específico e muda a cada peça; a extrusão é longa, genérica e nunca muda. Escrever uma engrenagem passa a ser escrever catorze grupos de quatro pontos, porque fechar tampas, costurar a casca e distribuir coordenadas já está feito e conferido. A divisão também decide onde procurar quando algo sai errado. Silhueta estranha é problema de perfil, e o perfil cabe numa dúzia de linhas legíveis. Silhueta certa com superfície estranha seria problema de extrusão — só que, se a extrusão estivesse errada, todas as peças estariam.
O eixo da oficina usa a mesma rotina de extrusão (a mesma, sem uma linha alterada), com um perfil regular de doze lados no lugar dos catorze dentes. Sai um cilindro, e nenhuma linha da rotina precisou ser tocada. À distância de trabalho ele convence; de perto, a faceta aparece, e cada lado responde à luz com um valor só. Conte os lados visíveis numa captura ampliada: se der doze, a normal continua decidida por face, como ficou combinado atrás.
Se a superfície aparecer contínua, alguém pediu à biblioteca normais suaves — e essa escolha vai valer também para os dentes da engrenagem. É por isso que a rotina de extrusão não tem opinião sobre suavização. Uma rotina que decidisse por todas acoplaria a aparência do eixo à da engrenagem.
a coordenada que acompanha o perímetro, em src/bancada/content/malha.ts
// Casca lateral. Cada aresta do perfil vira um retângulo com normal própria,
// perpendicular a ela: é isso que dá o dente com flanco chapado em vez de
// ombro arredondado. A coordenada horizontal da textura acompanha o perímetro
// percorrido, e a vertical vai da base ao topo.
let percorrido: number = 0;
A quarta lista é a que menos sentido faz escrever agora. As superfícies deste ponto do percurso são de cor lisa, e nenhuma imagem cai sobre peça alguma. A coordenada horizontal acompanha o perímetro percorrido e a vertical vai da base ao topo, e escrevê-la custa uma variável acumuladora. Adiar custa mais. O custo chega semanas depois, na mesa de quem for procurar o defeito.
A distribuição errada não produz sintoma enquanto não houver imagem. No dia em que houver, o sintoma engana: o desenho aparece esticado numa face e comprimido na vizinha. Quem vê isso procura o defeito na imagem, onde ele se manifesta, e ele está na geometria, escrita semanas antes. Há um caso em que adiar é a decisão certa. Peça que nunca vai receber imagem, como um volume auxiliar ou um marcador de depuração, não precisa de coordenada nenhuma.
1.3 O que a cena cobra, e o que ela declara
A conta de um quadro tem duas metades, e a segunda não é feita de geometria.
Cada quadro é um envelope de onze milissegundos, e o que não couber nele não fica para depois: some. O envelope foi declarado no capítulo anterior sobre uma cena de caixas cinzentas, quando havia folga a página inteira e nada de pesado a que atribuir culpa. Agora ele tem contra o que ser gasto.
Cada dente acrescenta dezesseis triângulos à malha final. A conta é curta: quatro pontos de contorno, cada um virando uma aresta da casca, cada aresta virando dois triângulos, mais as tampas. Passar de catorze para vinte e quatro dentes acrescenta cento e sessenta triângulos a uma peça só. Veja o outro lado da conta, que é o olho. A engrenagem grande mede quinze centímetros e é vista a cerca de meio metro. A essa distância, a diferença angular entre um dente e o vizinho fica abaixo do que a vista separa numa peça em movimento. Os vinte e quatro dentes chegam à placa de vídeo, são desenhados com o mesmo cuidado que os catorze, e não chegam a ninguém.
Repare no que essa frase não afirma. Ela não diz que mais triângulos sejam sempre desperdício. Diz que existe uma resolução acima da qual o acréscimo não chega ao olho. Ela depende de duas coisas concretas: o tamanho da peça e a distância de onde ela é olhada. Uma peça que quem usa aproxima do rosto a vinte centímetros pede mais dentes do que a mesma peça vista de meio metro.
As cinco peças da oficina somam 440 triângulos. São 224 na engrenagem grande, 144 na pequena, 48 no eixo e 12 em cada uma das duas primitivas (que continuam primitivas). Esse número é um censo, e a palavra faz diferença. Ele não é teto, não é meta, não é promessa de desempenho. O que a plataforma aguenta continua sem medição, e escrever aqui um número de orçamento seria inventá-lo. “A oficina tem 440 triângulos” se confere lendo uma lista; “a máquina aguenta tantos” só se sabe medindo a máquina com uma cena que a force. O censo serve para outra coisa. Daqui a um passo entram modelos com contagem decidida por outra pessoa, e a única forma de saber quanto eles acrescentaram é ter os 440 anotados hoje.
A segunda metade da conta costuma dominar a primeira. Desenhar mil triângulos com um material só custa bem menos do que desenhar os mesmos mil divididos em dez materiais. Um material é, para a placa de vídeo, um programa compilado com os seus parâmetros. Trocar de material entre dois lotes significa trocar o programa em execução. A troca tem custo fixo, independente de quantos triângulos venham depois dela. As quatro superfícies da oficina cobram quatro dessas trocas por quadro, e nem uma a mais.
flowchart LR
Q["o quadro<br/>envelope de onze milissegundos"]
Q --> T["primeira metade<br/>triângulos desenhados"]
Q --> E["segunda metade<br/>trocas de estado por material"]
T --> T1["440 triângulos na oficina<br/>224 na engrenagem grande"]
T --> T2["cada dente acrescenta<br/>dezesseis triângulos"]
E --> E1["material é programa compilado<br/>trocar custa por troca, não por triângulo"]
E --> E2["quatro superfícies para cinco peças<br/>as duas engrenagens dividem a mesma instância"]
T1 --> C["censo da cena construída<br/>linha de base para comparar depois"]
E2 --> C
Um lote grande dilui o custo fixo; muitos lotes pequenos o multiplicam pelo número de trocas que a cena exige em cada quadro. Disso decorre um hábito que vale mais do que qualquer otimização posterior: contenha o catálogo. A oficina inteira tem quatro superfícies para cinco peças, e a contenção foi decisão de projeto, não consequência de pressa.
Um juízo aqui é meu, e prefiro assiná-lo. Eu orço pelo mais apertado dos dois tetos do capítulo anterior, o do visor, mesmo escrevendo a cena na máquina de mesa. Folga desperdiçada na janela custa aparência; envelope estourado no visor custa mal-estar em quem está com o aparelho no rosto. O teto apertado governa as três decisões desta página. Catorze dentes em vez de vinte e quatro. Quatro superfícies em vez de nove. E a caixa no lugar da malha no teste de contato.
Como se diz a uma biblioteca gráfica que uma peça é de latão? Um caminho descreve o resultado, “um cinza brilhante”, e é o que quase todo primeiro exemplo faz. O outro descreve a matéria, “metálica, pouco rugosa”, e aceita que a cor final dependa de onde a peça está.
Uma superfície se declara por propriedades do material — quanto ele é metálico e quanto é rugoso —, e a aparência final é o que a luz da cena produz sobre essas propriedades.
Daí decorre uma dependência que a declaração sozinha esconde: a superfície metálica devolve ao olho o que existe em volta para refletir, e uma cena com pouca luz ao redor a deixa escura e sem leitura, mesmo com a declaração intacta.
Uma superfície declarada é uma instância, e a mesma instância pode ser apontada por quantas peças a usem. Duas instâncias idênticas em valor permanecem distintas para a placa de vídeo, que trata cada uma como um programa próprio e troca de estado uma vez por cada.
O exemplo mais curto é uma peça que atravessa dois ambientes. Primeiro a sala do computador de mesa, sob a iluminação que a cena declara. Depois o regime que preserva o mundo físico atrás dela, onde a luz é a da sala de verdade. A peça descrita por matéria muda nos dois e continua coerente nos dois. A descrita por cor fica idêntica, e é essa identidade que a denuncia — a única coisa em cena que não respondeu à luz. A definição não promete realismo. Dois parâmetros não descrevem tinta descascada, poeira, nem a diferença entre latão novo e latão de trinta anos de oficina. Esses acabamentos entram por imagem.
A dependência declarada pega quase todo mundo uma vez. Numa cena com uma fonte de luz só e nada em volta, o ambiente refletido é quase nada, e a peça metálica fica escura e chapada. Quem vê isso culpa o material, e nenhum ajuste nos dois parâmetros resolve, porque os dois estão corretos. O problema é da cena. A correção é luz ambiente, uma segunda fonte, ou um mapa de entorno.
Compare com a normal invertida da primeira seção. Lá a peça estava errada e a suspeita caía na luz; aqui a luz está incompleta e a suspeita cai na peça. Que sinal separa os dois casos? Ponha ao lado da suspeita uma superfície nada metálica e bem rugosa. Se ela também estiver escura, o problema é da iluminação; se só a metálica ficar escura, falta entorno para refletir.
a descrição com as duas colunas, em src/bancada/content/materiais.ts
O que importa ali é o último campo: o que a superfície cobra em tempo real, escrito em uma frase, ao lado do que ela entrega. Um tipo que exige as duas colunas cobra duas opiniões de quem acrescenta uma superfície. Uma sobre a aparência, outra sobre o consumo, as duas no mesmo instante.
As duas engrenagens dividem o mesmo latão, e dividem a mesma instância — a mesma referência, criada uma vez e apontada duas vezes. Criar duas instâncias de valores idênticos seria mais natural de escrever. E produziria dois programas distintos para a placa, com duas trocas de estado por quadro no lugar de uma. A placa não compara valores para descobrir que dois programas fazem a mesma coisa. Num catálogo de quatro isso é invisível em qualquer medição de hoje. O que está sendo formado aqui é o hábito. Ele cobra num catálogo de quarenta, com a cena inteira já escrita sobre a forma errada.
1.4 Autorar, pedir pronto, ou trazer de fora
Por quantos caminhos uma forma chega à cena? Três, e os três convivem no mesmo projeto sem contradição alguma: escrevê-la em código, pedi-la à biblioteca, trazê-la de uma ferramenta de autoria. A cena do capítulo anterior sobe primeiro, com a estrutura e o parentesco que ela já tinha. A forma entra depois, sobre a árvore pronta.
A operação que veste a cena percorre os nós que a montagem criou e substitui o conteúdo de cada um. Nenhum nó é criado, nenhum é removido, nenhum troca de pai. Essa restrição mantém cada assunto num lugar só, e há um ganho concreto além da arrumação: o estado anterior continua exibível. Exibi-lo ao lado da versão vestida separa duas perguntas que a versão vestida embaralha — se as peças estão nos lugares certos, e se elas parecem certas.
A operação também trata o nó que não é o que ela esperava: interrompe a execução e nomeia a peça, em vez de ignorar e seguir adiante. Aceitar em silêncio deixaria a peça na cena com a forma provisória. A diferença entre a caixa por decisão e a caixa por troca falhada desapareceria da tela.
Duas das cinco peças, aliás, recusam a geometria escrita à mão. O corpo do mecanismo é uma caixa e a tampa é uma placa, as duas pedidas prontas à biblioteca. Escrever à mão a malha de um paralelepípedo é um exercício de oito vértices e doze triângulos. O resultado é idêntico ao que a biblioteca já entregava, pelo mesmo custo em quadro. O único ganho é poder dizer que foi escrito à mão.
A fronteira defensável cabe numa frase: a geometria autorada vai onde a forma carrega significado. O dente que engrena carrega significado: o ângulo do flanco decide se a peça parece mecânica ou serrilhada. A faceta do eixo carrega, porque denuncia como as normais foram decididas. Um paralelepípedo não carrega nada além de ser um paralelepípedo. O critério se aplica peça a peça. Ele defende também contra o excesso oposto. Uma cena inteira de primitivas recebidas prontas pode estar correta em estrutura, custo e material, sem uma única decisão de forma tomada por quem a montou.
Justificativa se escreve no instante da decisão. Passadas duas semanas, o código deixa de distinguir a peça primitiva por escolha da peça primitiva porque ninguém chegou nela. Só quem estava na sala naquele dia sabe qual foi qual.
Antes de seguir. Escolha uma peça do seu projeto que hoje é um cubo ou um cilindro. Se alguém perguntasse por que ela não foi modelada, você teria uma resposta que não fosse “ainda não deu tempo”? A resposta separa a decisão do adiamento, e o código não guarda essa diferença sozinho.
Trocar a malha de um nó tem ainda um passo que não aparece na descrição da troca. A malha anterior ocupa memória na placa de vídeo, e essa memória não é recuperada pelo mecanismo que recupera a memória comum do programa. Por isso a rotina de vestir libera a malha antiga antes de instalar a nova. Numa troca isso não produz diferença observável (é o que torna a omissão fácil de cometer). O sintoma aparece em ambiente que recarrega conteúdo várias vezes na mesma sessão. A memória de vídeo se esgota, o navegador perde o contexto de desenho, e a tela fica preta sem mensagem alguma.
Uma malha exportada de ferramenta de autoria costuma chegar com três problemas, e os três são invisíveis do lado de lá. A contagem de triângulos, decidida para render fora do tempo real, onde um quadro pode levar minutos. O material, ajustado sob a iluminação da ferramenta, que a cena de destino não tem. E a escala, o mais banal e o mais frequente dos três. Exportar em centímetros para um ambiente que trabalha em metros chega cem vezes maior, com sintoma espetacular e imediato.
Os três têm origem comum, e nomeá-la ajuda mais do que decorar a lista. A ferramenta é um ambiente completo, com iluminação própria, unidade própria e destino de render próprio, e ajusta o modelo para o ambiente dela. Daí sai a única defesa consistente: conferir os três no destino, e não na origem. Abra o modelo no ambiente em execução e leia a contagem que ele acrescentou. Olhe-o sob a iluminação de verdade, e meça uma dimensão conhecida contra uma peça já existente.
Ferramentas de autoria não são um estágio anterior a superar, e escrever tudo à mão não é decisão correta em projeto algum de tamanho sério. O que se defende é a ordem. Escrever à mão uma vez, primeiro, para adquirir os nomes das coisas; trazer as ferramentas depois, com esses nomes na mão. Quem nunca escreveu uma normal não diagnostica a peça que chegou escura, porque a categoria não está no vocabulário disponível e a busca começa e termina na iluminação. Quem sabe pedir contagem compatível com tempo real, superfície ajustada sob a iluminação de destino e escala em metros está fazendo três pedidos verificáveis.
1.5 O que se vê e o que colide
A caixa contra a qual se testa contato é maior que a peça em toda direção.
Duas peças da oficina apresentam o mesmo sintoma para quem olha só a tela. Uma gruda na mão a um palmo de distância. A outra atravessa o encaixe como se ele não estivesse ali. A tela mostra a malha nos dois casos. O comportamento não vem da malha.
Veja bem como se estaciona um carro. Ele tem retrovisor, para-choque, espelho e maçaneta, e ninguém estaciona pensando em nenhum dos quatro. Pensa-se no retângulo que o carro ocupa, maior que o carro em toda direção, e ele responde rápido à única pergunta que interessa ali: se o carro cabe. Um modelo fiel, com espelhos e maçanetas, responderia com mais trabalho e pior resultado.
O volume de contato de uma peça é a forma simples contra a qual se testa proximidade e toque, mantida ao lado da malha que se exibe.
Ele se deriva da geometria da peça e vive no espaço local dela. Assim ele gira, escala e muda de pai junto com a peça, sem recálculo e sem que ninguém precise mantê-lo sincronizado.
O critério que decide a forma do volume é o comportamento pretendido na manipulação, e a semelhança com a silhueta é uma consequência ocasional desse critério. Testar contra a malha completa custa uma comparação por triângulo a cada consulta e entrega uma superfície cheia de reentrâncias, por onde a mira escapa em lugares que quem manipula não tem como prever.
A palavra que a definição põe no centro é comportamento. Semelhança e fidelidade ficam de fora. Um volume de contato bom é o que produz a interação pretendida, e se parece com a peça na medida em que isso ajuda. A leitura concorrente é atraente e chega primeiro — ela diz que o volume seria a malha simplificada, uma versão de menos triângulos da mesma forma. A caixa da engrenagem grande a derruba sozinha: mede 19,5 por 5,6 por 19,6 centímetros, cobre os vales entre os dentes e sobra dois centímetros em cada direção. Ninguém obtém aquilo simplificando malha alguma.
flowchart TB
NO["nó da peça na árvore da cena"]
NO --> MALHA["malha exibida<br/>224 triângulos, catorze dentes<br/>responde: o que o olho vê"]
NO --> VOL["volume de contato<br/>uma caixa, seis planos<br/>responde: a mira apanhou a peça"]
MALHA --> CRIT1["critério: a forma da peça de verdade"]
VOL --> CRIT2["critério: o comportamento pretendido<br/>na manipulação"]
VOL --> LOCAL["vive no espaço local do nó<br/>gira, escala e muda de pai junto, sem recálculo"]
VOL --> FOLGA["silhueta mais a folga<br/>em cada direção"]
FOLGA -.->|"grande demais"| D1["captura o que passou perto:<br/>a peça gruda no ar"]
FOLGA -.->|"pequena demais"| D2["recusa o que já encostou:<br/>a tentativa seguinte vem com força"]
A malha já existe e descreve a peça com exatidão, o que torna tentador usá-la no teste. O argumento do custo aparece primeiro e é o menos importante dos dois. A engrenagem grande tem 224 triângulos, e testar um ponto contra ela significa 224 comparações. Multiplique por cinco peças, dois ponteiros e noventa consultas por segundo, numa máquina de vídeo integrado.
O segundo argumento é mais forte, e não é de desempenho. Testar contra a malha exata está errado. Ninguém encaixa uma peça pelo flanco de um dente específico, e quem manipula mira a peça inteira, que para efeito de mira é a silhueta dela. A malha exata transforma cada vale entre dois dentes numa armadilha. O dedo entra pelo vale, o teste responde que não há contato, e a pega falha por um milímetro de deslocamento lateral.
A caixa é derivada da geometria da peça, e não do nó já montado na cena. A diferença parece de implementação e decide comportamento. Derivada da geometria, ela nasce no sistema de coordenadas da própria peça. Ali acompanha de graça tudo o que acontece com a peça: girar gira a caixa, escalar escala, prender a outro objeto leva junto. Ninguém escreve uma linha para manter as duas sincronizadas.
Medida a partir do nó montado, a caixa sairia em coordenadas de mundo, e o mundo se move: ela se desprenderia no primeiro deslocamento. Recalculá-la a cada quadro é pior, porque a caixa alinhada aos eixos do mundo cresce quando a peça se inclina. Uma engrenagem a quarenta e cinco graus ganharia um volume bem maior do que em repouso. A captura ficaria mais fácil num ângulo do que no outro. Nada no código explicaria por quê.
Há um segundo motivo para não medir o nó, mais sorrateiro. O contorno de depuração é filho do próprio nó, e é sempre um pouco maior que a caixa que ele desenha. Medir o nó inteiro incluiria o contorno na conta, e cada recálculo engordaria a caixa sobre a anterior. A peça ficaria mais fácil de apanhar a cada vez que alguém pedisse a medida.
1.6 Um número decide se a peça gruda no ar
Uma discussão sobre folga entre duas pessoas degenera rápido. Cada uma tem uma intuição sobre quanto é generoso demais, nenhuma tem argumento, e a conversa termina em preferência estética disfarçada de decisão técnica. Existe saída. Ela cabe num ponto de teste.
A folga é a distância acrescentada ao volume de contato em cada direção, além da extensão da malha que ele representa.
Ela erra para os dois lados, com sintomas opostos. Folga grande captura o que passou perto, e a peça responde a quem não a tocou. Folga pequena recusa o que já encostou, a tentativa seguinte é feita com mais força, e a força piora a mira.
A folga é decidida por peça, e o número anda junto da razão dele. Peça fina pede folga pequena: a folga que serve para apanhar uma peça grande cerca a peça fina de um volume feito quase só de ar, e qualquer mira que cruze a vizinhança a rouba do alvo pretendido.
Ponha um ponto de teste a 12 cm do centro da engrenagem grande, deslocado no eixo horizontal, que é o eixo em que a peça é mais larga. Doze centímetros é fora da peça com margem confortável, e ali a mão de quem usa ainda não encostou em nada. Com a folga adotada de 2,0 cm, a meia-extensão da caixa naquele eixo é de 9,8 cm. O volume não captura o ponto, que é o resultado pretendido. Com uma folga de 6,0 cm, a meia-extensão vai a 13,8 cm, e o volume captura. Esse segundo caso é a peça grudando em quem só passou perto, medido em vez de discutido, e ninguém discorda de um booleano.
A definição não oferece valor padrão, e a recusa é de quem a escreveu. Um número “que costuma servir” é exatamente aquele que ninguém revisa depois. Ele chega com autoridade de convenção, atravessa o projeto sem ser questionado, e produz o comportamento errado em metade das peças. O projeto que acompanha esta leitura derruba a execução quando uma peça manipulável não tem folga declarada, e derruba de propósito.
Olhe dois números lado a lado. O eixo da oficina leva folga de 0,8 cm, a menor da lista, e a caixa dele mede 4,0 por 23,6 por 4,0 centímetros. A engrenagem grande leva 2,0 cm. Por que o eixo leva menos da metade? Porque ele é fino. Aplicar nele os dois centímetros da peça maior daria uma caixa quase toda vazia. Uma mira que passasse a dois centímetros dele o capturaria, disputando com a peça que estava de fato sendo mirada atrás dele.
Esse é o defeito que mais irrita quem usa, porque não parece defeito. A peça errada é apanhada, quem manipula conclui que errou a mira, tenta outra vez com mais cuidado, e apanha a peça errada de novo. O ambiente parece impreciso. Está fazendo exatamente o que foi mandado fazer.
Antes de seguir. Duas peças paradas na bancada, cujos volumes se interpenetram, vão brigar pela mesma pega no dia em que houver pega. Como você descobre isso hoje, antes de existir qualquer manipulação, sem abrir o código? A resposta custa um botão.
Onde mora a justificativa de uma folga? A resposta natural é o comentário ao lado da constante, e ela é insuficiente por um motivo mecânico: comentário não sai do arquivo. Quem lê um relatório de volumes vê a folga de 0,8 cm e não tem como saber se aquele número foi pensado ou digitado.
o volume registrado com a folga e a razão dela, em src/bancada/content/colisao.ts
export interface VolumeDeContato {
/** O nó cuja forma visível este volume representa. */
readonly no: Mesh;
/** Meia-extensão da caixa, no espaço local do nó, já com a folga somada. */
readonly meiaExtensao: Vector3;
/** Centro da caixa no espaço local do nó. Raramente é a origem: malha assimétrica desloca. */
readonly centroLocal: Vector3;
/** Folga adotada, em metros, e a justificativa dela em uma frase. */
readonly folga: number;
readonly razaoDaFolga: string;
}
A razão viaja com o número até o relatório, guardada como dado no mesmo registro. O efeito mais útil dessa estrutura está na obrigação que ela cria: quem escolhe um número precisa escrever a razão na mesma linha, no instante da escolha. Um campo de texto vazio é visível; uma justificativa que nunca existiu na cabeça de ninguém não é. A prática tem limite (nenhuma estrutura de dados impediria): ela não impede que alguém escreva “porque sim” no campo. O que ela produz é uma frase ruim e legível no lugar de uma ausência silenciosa.
Duas outras decisões acompanham a caixa. A folga é somada uma vez, na criação, e não a cada teste. Guardar a meia-extensão crua daria três somas por consulta, multiplicadas pelo número de volumes e de ponteiros ativos. Somando na criação, o teste de contenção fica com três comparações e nada mais.
A segunda é o instrumento. Cada volume ganha um contorno em linha, filho do próprio nó da peça, desligado por padrão e ligado por um botão. Com ele ligado, os dois defeitos que tinham o mesmo sintoma ficam distintos em um olhar. A peça que gruda longe aparece envolvida por uma caixa que engole a vizinha. A que atravessa o encaixe aparece com uma caixa menor que a própria silhueta.
O contorno é criado sempre, junto com a caixa, e nasce escondido. Montá-lo sob demanda economizaria memória, e mudaria o que ele desenha: passaria a mostrar o estado do momento em que o pedido chegou, recalculado ali. Instrumento de depuração que altera o que ele mede é pior do que instrumento nenhum, porque produz confiança onde não deveria haver. O botão vive na página, e não dentro da cena. A razão é de ordem, e é temporária: ainda não existe ponteiro (e sem ponteiro não há como apertar nada dentro do ambiente).
O contorno tem um limite, e ele aparece quando alguém precisa comparar duas caixas. A olho, elas parecem próximas ou parecem distantes; ninguém estima dois centímetros olhando uma projeção em perspectiva numa tela. Por isso o instrumento tem uma segunda metade, e ela é texto. O relatório traz cada volume em centímetros, com as três medidas, a folga adotada e a razão dela ao lado.
A divisão de trabalho entre os dois instrumentos
O contorno responde onde: que caixa está no lugar errado, que duas se sobrepõem, que uma ficou menor que a peça. O relatório responde quanto: se a diferença é de dois centímetros ou de dois milímetros, e se a folga que produziu aquilo tinha razão. Uma pergunta é espacial e se responde olhando; a outra é numérica e não se responde olhando, por mais treinado que seja quem olha. O relatório é lido por quem não está com o ambiente aberto, e deixa de fora exatamente uma coisa: se duas caixas se cruzam. Cruzamento depende da posição de cada peça na cena, e a lista traz medidas locais.
O critério de decisão que sobra daqui serve a qualquer instrumento que você venha a construir adiante. Quando um medidor puder ser construído no momento da leitura ou mantido desde o registro, escolha o segundo, e pague a memória. Quando uma pergunta for espacial e outra for numérica, construa dois instrumentos, e não um que tente as duas.
1.7 O que fica escrito para o próximo passo
O mais caro que a oficina ganhou aqui não cabe em fotografia nenhuma.
Volte à engrenagem do início, a dos catorze dentes com flanco chapado. Agora ela tem nome para as quatro listas que a formam. Tem razão para o número de dentes, superfície declarada por matéria, e uma caixa invisível com dois centímetros de folga justificada. A segunda imagem que eu mandei guardar era essa caixa, e ela continua sem aparecer em captura nenhuma.
flowchart TB
F["as seis conferências do estado"]
F --> V["três exigem movimento"]
F --> L["duas exigem ler uma lista"]
F --> I["uma exige ligar um instrumento"]
V --> V1["girar cada peça contra a luz:<br/>nenhuma lateral escurece ao encarar a fonte"]
V --> V2["olhar o eixo ampliado:<br/>doze lados, normal decidida por face"]
V --> V3["mover a peça e o pai dela:<br/>o volume acompanha sem recálculo"]
L --> L1["densidade com razão que cita<br/>tamanho da peça e distância de leitura"]
L --> L2["cada folga com a justificativa<br/>na mesma linha do número"]
I --> I1["contorno ligado por comando:<br/>nenhum par de caixas se sobrepõe em repouso"]
Parte das peças tem geometria escrita no projeto, e a escolha entre autorar e pedir à biblioteca tem razão dita em voz alta. Nenhuma face está com a normal invertida. Cada peça foi girada devagar contra a luz direcional, e a lateral escureceu quando devia clarear em nenhuma delas. A densidade de cada malha tem justificativa que menciona o tamanho da peça e a distância de onde ela é vista.
Os materiais são compartilhados entre peças de mesma superfície, e o catálogo cabe num olhar. Toda peça manipulável tem volume de contato, e cada folga tem razão escrita ao lado do número. Os volumes podem ser exibidos por comando, e nenhum par de caixas se sobrepõe com as peças em repouso.
Nenhuma das seis se faz olhando uma captura de tela: três exigem movimento, duas exigem ler uma lista, e uma exige ligar um instrumento. A das normais tem o melhor retorno por minuto gasto. Girar cinco peças contra a luz leva menos de dois minutos, e elimina a tarde descrita lá atrás. Ela precisa ser refeita a cada peça nova, porque o defeito nasce com a peça.
O trabalho aqui se divide em duas metades de destinos diferentes. Malha, perfil, densidade e superfície foram gastas e entregues, e a oficina parece uma oficina. Os cinco volumes, as cinco folgas e o comando que as exibe foram guardados, e não farão nada até que exista alguém apontando para uma peça. As duas também se conferem em ritmos diferentes. A forma, uma vez, e de novo só se o perfil mudar. O volume, sempre que a peça mudar de tamanho, de posição ou de vizinhança.
Escrever o volume agora custa decidir uma folga e digitar uma linha por peça. Escrevê-lo depois, com a manipulação já implementada, custa voltar a um código que funcionava e acrescentar nele uma camada que ele não previa. Essa é a aritmética inteira: uma linha por peça hoje, contra uma camada nova num código que já funciona.
Sobra uma assimetria desconfortável, sem solução elegante. A primeira metade se defende sozinha, porque a oficina está ali com dentes e facetas. A segunda só se defende por argumento. O que ela produziu foi a ausência futura de um conjunto de problemas. Nenhum deles é exibível: a peça que não gruda no ar, a mira que não rouba o alvo vizinho, a sessão de depuração que não aconteceu. Ninguém aplaude um problema que não aconteceu.
O que vem em seguida cobra as duas, em ordem. Primeiro entram modelos vindos de fora, com contagem decidida por outra pessoa, e os 440 anotados aqui viram o único termo de comparação disponível. Depois cada caixa invisível ganha, enfim, algo contra o que ser testada.