diff --git a/README.md b/README.md
index 7687f0e..bacae26 100644
--- a/README.md
+++ b/README.md
@@ -130,8 +130,8 @@ O **Isometricon** foi planejado com interfaces limpas no módulo `src/integratio
| Módulo | Equipe A (Motor do Mundo) | Equipe B (Motor Interativo) |
| :--- | :--- | :--- |
| **Responsabilidade** | Chunks, VoxelGrid, Terreno, Shaders, Otimização | Raycasting, Grid, Miniaturas, UI |
-| **Fornece** | `VoxelGridProvider`, `CameraStateProvider` | Comandos de seleção e movimento |
-| **Consome** | Coordenadas de foco e seleção da Equipe B | `VoxelGrid`, `ViewMatrix`, `ProjectionMatrix` |
+| **Fornece** | `VoxelGridProvider` e estado de frame no loop principal | Raycasting, grid, miniaturas e destaques |
+| **Consome** | Dados de terreno por `VoxelGridProvider` | View, Projection, Model e viewport passados diretamente |
> Consulte [docs/INTEGRATION_SPEC.md](docs/INTEGRATION_SPEC.md) para a especificação completa do contrato.
diff --git a/docs/EQUIPE_B_ARCHITECTURE.md b/docs/EQUIPE_B_ARCHITECTURE.md
index 88b0c61..efb931e 100644
--- a/docs/EQUIPE_B_ARCHITECTURE.md
+++ b/docs/EQUIPE_B_ARCHITECTURE.md
@@ -25,7 +25,7 @@ O **Motor Interativo (Interactive Engine)** é o subprojeto da **Equipe B** resp
+-----------------------------+ +-------------------------------+
| |
+----------- src/integration/ ----------+
- VoxelGridProvider / Bridge
+ VoxelGridProvider
```
---
@@ -40,11 +40,9 @@ src/
│ ├── grid_overlay.py # Renderização do grid quadriculado
│ ├── token_manager.py # Gerenciamento de miniaturas/tokens
│ └── ui_renderer.py # Overlay de UI (fichas de RPG)
-├── integration/ # 🔌 Ponte Equipe A <-> Equipe B
+├── integration/ # 🔌 Consulta compartilhada A <-> B
│ ├── __init__.py
-│ ├── voxel_provider.py # VoxelGridProvider (implementação)
-│ ├── highlight_bridge.py # HighlightBridge (ativa hover shader)
-│ └── camera_provider.py # CameraStateProvider (matrizes)
+│ └── voxel_grid.py # VoxelGridProvider (implementação)
assets/
├── shaders/
│ ├── highlight.vert # 🟦 Shader de destaque/hover (vertex)
@@ -315,9 +313,9 @@ Consulte o documento completo em [docs/INTEGRATION_SPEC.md](INTEGRATION_SPEC.md)
| Interface | Provedor | Consumidor | Finalidade |
|-----------|----------|------------|------------|
-| `VoxelGridProvider` | Equipe A | Equipe B | Consulta blocos e colisão |
-| `HighlightBridge` | Equipe A | Equipe B | Ativa hover shader |
-| `CameraStateProvider` | Equipe A | Equipe B | Matrizes View/Proj para raycasting |
+| `VoxelGridProvider` | Motor do Mundo | Motor Interativo | Consulta blocos carregados |
+| Matrizes de frame | `src.main` | Raycast e renderizadores | View, Projection, Model e viewport passados diretamente |
+| Renderizadores de overlay | Motor Interativo | Loop principal | Controlam hover e visibilidade da grade diretamente |
---
diff --git a/docs/INTEGRATION_SPEC.md b/docs/INTEGRATION_SPEC.md
index e515aff..09ee589 100644
--- a/docs/INTEGRATION_SPEC.md
+++ b/docs/INTEGRATION_SPEC.md
@@ -1,283 +1,94 @@
-# 🔌 Especificação Completa do Contrato de Integração (Equipe A ↔ Equipe B)
+# Contrato de Integração Atual (Motor do Mundo ↔ Motor Interativo)
-
+## Visão geral
-[](ARCHITECTURE.md)
-[](EQUIPE_B_ARCHITECTURE.md)
-
-
-
----
-
-## 1. Visão Geral
-
-Para garantir total independência de desenvolvimento durante os 30 dias e uma integração final simples e sem atritos, definem-se os seguintes contratos de dados e serviços entre a **Equipe A (Motor do Mundo)** e a **Equipe B (Motor Interativo)**.
-
-O módulo de ponte está em `src/integration/` e é mantido pela **Equipe A**, mas consumido pela **Equipe B**.
-
----
-
-## 2. Diagrama de Comunicação
+O Isometricon executa o Motor do Mundo e o Motor Interativo no mesmo processo e
+no mesmo loop de frame. O contrato de dados entre as camadas é
+`src.integration.VoxelGridProvider`; não há bridges de câmera ou de destaque.
```
-┌───────────────────────────────────┐ ┌───────────────────────────────────┐
-│ EQUIPE A (Motor do Mundo) │ │ EQUIPE B (Motor Interativo) │
-│ │ │ │
-│ IsometricCamera ──────────────────────► CameraStateProvider │
-│ │ │ (view, proj, viewport) │
-│ │ │ │ │
-│ VoxelWorld / Chunks ──────────────────► VoxelGridProvider │
-│ │ │ (getBlockAt, isSolid, getTop) │
-│ │ │ │ │
-│ HighlightRenderer ◄───────────────────── HighlightBridge │
-│ (shader ativado pela Equipe A) │ │ (setHighlight, clearHighlight) │
-│ │ │ │ │
-│ GridOverlay ◄─────────────────────────── setGridOverlayVisible │
-└───────────────────────────────────┘ └───────────────────────────────────┘
-```
-
----
-
-## 3. Contrato 1 — Acesso a Dados do Terreno (`VoxelGridProvider`)
-
-A Equipe A expõe métodos de consulta rápida para que a Equipe B possa realizar Raycasting 3D e validar posicionamento de miniaturas.
-
-### Interface Python
-
-```python
-# src/integration/voxel_provider.py
+WorldManager ──neighbor_at──► VoxelGridProvider ──► raycast_voxels
+ │ │
+ ├── chunks carregados ──► GridOverlayRenderer ▼
+ └── terreno renderizado ◄── BlockHighlightRenderer / PlayerToken
-class VoxelGridProvider:
- """
- Interface de consulta espacial do VoxelGrid para uso da Equipe B.
- Implementada pela Equipe A, consumida pela Equipe B.
- """
-
- def get_block_at(self, world_x: int, world_y: int, world_z: int) -> int:
- """
- Retorna o tipo do bloco (BlockType) na coordenada do mundo.
- Retorna 0 (AIR) para coordenadas fora dos limites do mundo.
- """
- ...
-
- def is_solid(self, world_x: int, world_y: int, world_z: int) -> bool:
- """
- Retorna True se o bloco em (x, y, z) é sólido (não-ar).
- Usado pela Equipe B para validar movimento de tokens.
- """
- ...
-
- def get_top_solid_block(self, world_x: int, world_z: int) -> int:
- """
- Retorna a coordenada Y do bloco sólido mais alto na coluna (x, z).
- Usado para posicionar tokens sobre o terreno.
- """
- ...
-
- def get_block_bounding_box(self, world_x: int, world_y: int, world_z: int) -> AABB:
- """
- Retorna a AABB (Axis-Aligned Bounding Box) do bloco em (x, y, z).
- Usado pelo algoritmo de ray-AABB intersection.
- """
- ...
-
- def get_world_bounds(self) -> tuple[int, int, int]:
- """
- Retorna (width, height, depth) do mundo em blocos.
- """
- ...
+IsometricCamera ──► View, Projection, Model, viewport ──► raycasting e renderizadores
```
-### Tipos de Bloco (`BlockType`)
-
-| ID | Nome | Sólido |
-|:--- |:--- |:---: |
-| `0` | `AIR` | ❌ |
-| `1` | `DIRT` | ✅ |
-| `2` | `GRASS` | ✅ |
-| `3` | `STONE` | ✅ |
-| `4` | `WATER` | ❌ |
-| `5` | `WOOD` | ✅ |
-| `6` | `LEAVES` | ❌ |
-
----
-
-## 4. Contrato 2 — Estado da Câmera (`CameraStateProvider`)
-
-Para que a Equipe B consiga converter coordenadas de tela $(x_{mouse}, y_{mouse})$ em um raio 3D, a Equipe A expõe as matrizes ativas da câmera isométrica por frame.
-
-### Interface Python
+## Consulta de terreno: `VoxelGridProvider`
```python
-# src/integration/camera_provider.py
-
-class CameraStateProvider:
- """
- Exporta estado atual da câmera isométrica para uso do raycasting da Equipe B.
- Deve ser atualizada a cada frame antes do loop de renderização da Equipe B.
- """
-
- def get_view_matrix(self) -> np.ndarray:
- """Retorna a matriz View 4x4 (float32) da câmera isométrica."""
- ...
-
- def get_projection_matrix(self) -> np.ndarray:
- """Retorna a matriz Projection 4x4 ortográfica (float32)."""
- ...
+from src.integration import VoxelGridProvider
- def get_viewport(self) -> tuple[int, int, int, int]:
- """Retorna (x, y, width, height) da viewport OpenGL atual."""
- ...
-
- def get_camera_target(self) -> np.ndarray:
- """Retorna o ponto de foco da câmera (vec3) no espaço do mundo."""
- ...
+provider = VoxelGridProvider(block_lookup=world_manager.neighbor_at)
```
-### Matemática do Raycasting (Projeção Ortográfica)
-
-Para projeção ortográfica, pixels diferentes produzem origens diferentes e
-direções paralelas. O raycast desprojeta os dois planos de recorte. Como o
-renderer gira o tabuleiro com uma matriz Model animada, usa-se a mesma matriz
-na composição $\mathbf{Q}=\mathbf{P}\mathbf{V}\mathbf{M}$:
-
-$$x_{NDC} = \frac{2 \cdot x_{mouse}}{W} - 1, \qquad y_{NDC} = 1 - \frac{2 \cdot y_{mouse}}{H}$$
-
-$$\mathbf{p}_{near}=\operatorname{dehom}\left(\mathbf{Q}^{-1}
-(x_{NDC},y_{NDC},-1,1)^T\right)$$
+O provider recebe e retorna coordenadas globais `(x, y, z)`. Para testes ou
+mundos estáticos, pode receber um mapeamento de `Chunk3D`; para o mundo em
+produção, `block_lookup=WorldManager.neighbor_at` consulta somente os chunks
+atualmente carregados.
-$$\mathbf{p}_{far}=\operatorname{dehom}\left(\mathbf{Q}^{-1}
-(x_{NDC},y_{NDC},1,1)^T\right)$$
+| Método | Contrato |
+|---|---|
+| `get_block_at(x, y, z)` | Retorna `BlockType`; regiões/chunks ausentes retornam `AIR`. |
+| `is_solid(x, y, z)` | Retorna se a célula está ocupada, isto é, se é diferente de `AIR`. |
+| `get_top_solid_block(x, z)` | Retorna o maior Y não-`AIR` na coluna carregada, ou `-1` se ela não existir. No streaming atual, a busca cobre a coluna vertical carregada `y=0` (0–15). |
+| `get_block_bounding_box(x, y, z)` | Retorna a AABB unitária `[x,x+1] × [y,y+1] × [z,z+1]`. |
-$$\mathbf{o}=\mathbf{p}_{near}$$
+As conversões para chunk/local usam divisão inteira e módulo, preservando
+coordenadas negativas. As consultas não iniciam carregamento, não aguardam a
+worker e não alteram o estado de streaming. O mundo é streamado e não possui
+bounds globais fixos.
-$$\mathbf{d}=\operatorname{normalize}(\mathbf{p}_{far}-\mathbf{p}_{near})$$
+### Ocupação não é superfície tática
-O resultado fica no espaço lógico dos voxels, coerente com as coordenadas
-globais do `VoxelGridProvider` durante pan, zoom, resize e rotação Q/E.
+Para o provider e o raycasting padrão, todo bloco diferente de `AIR` é
+ocupado/atingível, incluindo `WATER` e `LEAVES`. A política de uma célula
+capaz de sustentar tokens e grade está em `src.interaction.surface`:
+`DIRT`, `GRASS`, `STONE` e `WOOD` são superfícies táticas; água e folhas não.
----
+## Estado de câmera e frame
-## 5. Contrato 3 — Renderização de Destaque (`HighlightBridge`)
-
-Quando a Equipe B detectar a interseção do raio do mouse com um bloco $(x, y, z)$, ela aciona a renderização de destaque via esta interface.
-
-### Interface Python
+No frame, `src.main` calcula diretamente:
```python
-# src/integration/highlight_bridge.py
-
-class HighlightBridge:
- """
- Permite que a Equipe B ative/desative efeitos visuais de seleção
- nos shaders de destaque implementados pela Equipe A/B.
- """
-
- def set_highlighted_block(self,
- world_x: int, world_y: int, world_z: int,
- color: np.ndarray) -> None:
- """
- Define o bloco atualmente sob foco do cursor (hover).
- color: vec4 (r, g, b, a) com valores em [0.0, 1.0]
- """
- ...
-
- def set_selected_block(self,
- world_x: int, world_y: int, world_z: int,
- color: np.ndarray) -> None:
- """Define o bloco selecionado (clique confirmado)."""
- ...
-
- def set_movement_range(self, cells: list[tuple[int, int, int]],
- color: np.ndarray) -> None:
- """
- Define a lista de células alcançáveis no turno atual.
- Exibidas com cor diferente para indicar alcance de movimento.
- """
- ...
-
- def clear_highlight(self) -> None:
- """Remove todos os destaques ativos."""
- ...
-
- def set_grid_overlay_visible(self, visible: bool) -> None:
- """Ativa ou desativa a renderização do grid quadriculado."""
- ...
-```
-
----
-
-## 6. Protocolo de Sincronização por Frame
-
-A ordem de execução por frame quando ambas as equipes estão integradas:
-
+projection = camera.get_projection_matrix(window.width, window.height)
+view = camera.get_view_matrix()
+model = camera.get_animated_model_matrix()
+mouse_x, mouse_y = window.get_cursor_pos_framebuffer()
```
-┌─── LOOP PRINCIPAL ─────────────────────────────────────────────────────────┐
-│ │
-│ 1. [Input] Coletar eventos de mouse e teclado │
-│ │
-│ 2. [Equipe B] Raycasting → detectar bloco sob cursor │
-│ │
-│ 3. [Bridge] HighlightBridge.set_highlighted_block(x, y, z, color) │
-│ │
-│ 4. [Equipe A] Renderizar mundo (chunks, terreno, shaders de iluminação) │
-│ │
-│ 5. [Equipe B] Renderizar overlays (highlight, grid, tokens, UI) │
-│ ↑ Deve vir DEPOIS da Equipe A para sobrepor corretamente │
-│ │
-│ 6. [Swap] Apresentar framebuffer final │
-└────────────────────────────────────────────────────────────────────────────┘
-```
-
----
-
-## 7. Opção de Integração via Sockets TCP (Avançado)
-
-Para integração de **processos separados** (cada equipe com seu próprio executável), as equipes podem usar TCP/IP local:
-
-```
-┌─────────────────────┐ TCP localhost:9999 ┌─────────────────────┐
-│ Equipe A │ ──────────────────────► │ Equipe B │
-│ (Servidor) │ ◄────────────────────── │ (Cliente) │
-│ Porta: 9999 │ │ │
-└─────────────────────┘ └─────────────────────┘
-
-Mensagens JSON:
- A → B: { "type": "world_state", "chunks": [...], "camera": {...} }
- B → A: { "type": "highlight", "block": [x, y, z], "color": [...] }
- B → A: { "type": "token_move", "token_id": "...", "target": [x, y, z] }
-```
-
-### Protocolo de Mensagens
-| Direção | Tipo | Payload | Frequência |
-|:--- |:--- |:--- |:--- |
-| A → B | `camera_state` | `view[16], proj[16], viewport[4]` | Por frame |
-| A → B | `world_ready` | `bounds[3], chunk_list` | Uma vez |
-| B → A | `highlight_set` | `x, y, z, color[4]` | Por evento |
-| B → A | `highlight_clear` | — | Por evento |
-| B → A | `grid_toggle` | `visible: bool` | Por evento |
+Esses valores são passados diretamente a `screen_to_world_ray`,
+`WorldManager.render`, `GridOverlayRenderer.render` e
+`BlockHighlightRenderer.render`. O raycasting usa a inversa de
+`Projection @ View @ Model`, mantendo o espaço lógico de voxels consistente
+durante pan, zoom, resize e rotação Q/E. Uma `CameraStateProvider` duplicaria
+esse estado sem adicionar um limite arquitetural útil.
----
+## Highlight e grid
-## 8. Checklist de Integração
+O loop principal calcula o hover por raycast e chama
+`BlockHighlightRenderer.render` diretamente quando há um hit. Sem hit, nenhum
+destaque é desenhado; durante pan, o hover é suspenso. O grid é controlado por
+`GridOverlayRenderer` e sua visibilidade é alternada diretamente pela tecla
+`G`; sua malha é atualizada a partir do snapshot e da revisão dos chunks
+carregados.
-### Equipe A entrega para Equipe B:
-- [ ] `VoxelGridProvider` funcional com mundo gerado
-- [ ] `CameraStateProvider` atualizado por frame
-- [ ] `HighlightBridge` ativa shader de destaque
-- [ ] Documentação de coordenadas do mundo (sistema de eixos)
+Portanto, `HighlightBridge` não faz parte do contrato atual. Seleção
+persistente, alcance de movimento e click-to-move são funcionalidades futuras
+e não são inferidas da existência de raycasting, token ou highlight de hover.
-### Equipe B entrega para Equipe A:
-- [ ] Shaders `highlight.vert/frag` compiláveis
-- [ ] Shaders `grid.vert/frag` compiláveis
-- [ ] Interface de teste para demonstrar raycasting
+## Ordem por frame
-### Critério de Integração Final:
-- [ ] Clicar no terreno da Equipe A destaca o bloco com shader da Equipe B
-- [ ] Token da Equipe B se move pelo terreno gerado pela Equipe A
-- [ ] Grid da Equipe B acompanha a câmera da Equipe A
+1. Processar entrada, atualizar token, câmera e streaming.
+2. Obter View, Projection, Model, viewport e cursor em pixels de framebuffer.
+3. Fazer raycast contra o `VoxelGridProvider` dos chunks carregados.
+4. Renderizar o mundo.
+5. Renderizar grid, destaques e token com as mesmas matrizes.
+6. Apresentar o framebuffer.
----
+## Verificação
-*Documento de contrato mantido em conjunto pelas **Equipes A e B** | CEFET-MG — Computação Gráfica*
+Os testes de integração cobrem provider estático, limites de chunk,
+coordenadas negativas, AABB, consulta dinâmica via `WorldManager` e unload de
+chunks. Os testes de raycasting cobrem unprojection, DDA e o provider dinâmico.
diff --git a/src/integration/README.md b/src/integration/README.md
index 7a077be..2976e6c 100644
--- a/src/integration/README.md
+++ b/src/integration/README.md
@@ -1,4 +1,4 @@
-# 🔌 Módulo de Integração (Team B Bridge)
+# 🔌 Módulo de Integração
Ponto de contato entre o Motor do Mundo (Equipe A) e o Motor Interativo
(Equipe B).
@@ -15,7 +15,11 @@ usando coordenadas globais `(x, y, z)`.
Para mundos estáticos, ele recebe um mapeamento de chunks. Para o mundo com
streaming, `block_lookup=world_manager.neighbor_at` mantém as consultas ligadas
aos chunks atualmente carregados. Uma região ausente continua retornando
-`AIR`; a consulta não carrega chunks nem aguarda o worker assíncrono.
+`AIR`; nenhuma consulta carrega chunks nem aguarda o worker assíncrono.
+
+`get_top_solid_block(x, z)` percorre somente a coluna vertical atualmente
+usada pelo `WorldManager` (chunk `y=0`) e também retorna `-1` quando a coluna
+não está carregada. O mundo streamado não possui bounds globais fixos.
### Consultas
@@ -39,6 +43,19 @@ As coordenadas fornecidas à interface são sempre coordenadas globais.
A conversão para chunk/local é feita internamente e suporta coordenadas
negativas.
+### Semântica de ocupação e superfície
+
+`is_solid()` significa "bloco ocupado": qualquer valor diferente de `AIR`,
+inclusive `WATER` e `LEAVES`. A superfície tática válida para tokens e grade
+é uma política separada de `src.interaction.surface`.
+
+### Integração de frame
+
+O estado de câmera/frame é passado diretamente pelo loop principal ao
+raycasting e aos renderizadores. Hover/highlight e visibilidade da grade são
+controlados diretamente por `BlockHighlightRenderer` e `GridOverlayRenderer`;
+não há `CameraStateProvider` nem `HighlightBridge` no contrato atual.
+
### AABB
Cada voxel ocupa:
diff --git a/src/integration/voxel_grid.py b/src/integration/voxel_grid.py
index 018ce72..682a119 100644
--- a/src/integration/voxel_grid.py
+++ b/src/integration/voxel_grid.py
@@ -129,10 +129,21 @@ def get_top_solid_block(
world_x: int,
world_z: int,
) -> int:
- # Retorna o maior Y sólido existente na coluna X/Z
- # A busca utiliza somente chunks carregados, caso não exista nenhum bloco sólido na coluna, retorna -1
+ """Retorna o maior Y não-AIR da coluna carregada em ``(x, z)``.
+
+ O ``WorldManager`` atual transmite apenas colunas no chunk vertical
+ ``y=0``. No modo dinâmico, a busca consulta essa coluna diretamente
+ pelo ``block_lookup``: regiões descarregadas continuam retornando AIR
+ e a consulta não interage com as filas de streaming.
+ """
x = _coordinate(world_x)
z = _coordinate(world_z)
+ if self._block_lookup is not None:
+ for y in range(Chunk3D.SIZE - 1, -1, -1):
+ if self.get_block_at(x, y, z) != BlockType.AIR:
+ return y
+ return -1
+
best_y: int | None = None
for chunk in self._chunks.values():
chunk_x_min = chunk.chunk_x * Chunk3D.SIZE
diff --git a/src/interaction/README.md b/src/interaction/README.md
index 4fb16a2..5cbacb5 100644
--- a/src/interaction/README.md
+++ b/src/interaction/README.md
@@ -15,7 +15,7 @@ Este pacote contém toda a lógica de interação do usuário com o tabuleiro:
## Dependências
-- `src/integration/` — Pontos de contato com a Equipe A (VoxelGridProvider, HighlightBridge, CameraStateProvider)
+- `src/integration/` — Consulta de terreno compartilhada (`VoxelGridProvider`)
- `src/rendering/` — TexturedMesh para renderização dos tokens
- `src/camera/` — IsometricCamera para cálculos de raycasting
diff --git a/src/interactive/README.md b/src/interactive/README.md
index 41bb86c..2515c7c 100644
--- a/src/interactive/README.md
+++ b/src/interactive/README.md
@@ -66,17 +66,18 @@ O diretório `src/interactive/` contém todos os módulos da **Equipe B (Motor I
## Arquitetura de Integração
```
-src/integration/ ← Bridge A↔B (mantido pela Equipe A)
- voxel_provider.py ← Consulta de blocos do mundo
- camera_provider.py ← Estado da câmera (view/proj)
- highlight_bridge.py ← Ativa shaders de destaque
+src/integration/
+ voxel_grid.py ← VoxelGridProvider: consulta de blocos do mundo
src/interactive/ ← DOMÍNIO DA EQUIPE B
- raycasting.py
highlight.py
grid_overlay.py
token_system.py
- ui_overlay.py
```
-Consulte [INTEGRATION_SPEC.md](../../docs/INTEGRATION_SPEC.md) para o contrato completo de interfaces.
+O loop principal passa View, Projection, Model e viewport diretamente ao
+raycasting e aos renderizadores. Highlight e grid mantêm seu próprio estado;
+`CameraStateProvider` e `HighlightBridge` não fazem parte da arquitetura atual.
+Seleção persistente, alcance de movimento e click-to-move continuam futuros.
+
+Consulte [INTEGRATION_SPEC.md](../../docs/INTEGRATION_SPEC.md) para o contrato completo.
diff --git a/src/world/README.md b/src/world/README.md
index cad33a5..d1ccdb9 100644
--- a/src/world/README.md
+++ b/src/world/README.md
@@ -43,9 +43,9 @@ Responsável pelo armazenamento, geração e otimização dos dados de blocos do
permanecem inteiras. O cubo de demonstração centrado em main.py precisará
ser posicionado no centro da célula pela futura renderização.
-O contrato VoxelGridProvider permanece futuro: consultas globais, solidez,
-topo de coluna e AABB por bloco serão compostas sobre esta fundação.
-A representação AABB contém somente dados; raycasting permanece fora deste módulo.
+`VoxelGridProvider` já compõe sobre esta fundação consultas globais, ocupação,
+topo de coluna e AABB por bloco. A representação AABB contém somente dados;
+raycasting permanece fora deste módulo.
## Meshing CPU (Issue #6)
@@ -279,4 +279,3 @@ validação da API e geometria/índices do mesher sem contexto gráfico.
Fora desta etapa: WorldManager/streaming, save/load, biomas, vegetação,
estruturas, greedy meshing, frustum culling, materiais avançados e Equipe B.
-
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 58cebd8..b44c478 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -4,7 +4,7 @@
from src.math.aabb import AABB
from src.integration import VoxelGridProvider
-from src.world import BlockType, Chunk3D
+from src.world import BlockType, Chunk3D, TerrainGenerator, WorldManager
def make_chunk(
@@ -98,6 +98,28 @@ def test_top_solid_block_across_y_chunks():
assert provider.get_top_solid_block(3, 4) == 16
+def test_top_solid_block_uses_loaded_dynamic_world_chunks():
+ generator = TerrainGenerator(seed=27, enable_caves=False)
+ manager = WorldManager(
+ generator=generator,
+ render_distance=0,
+ create_gl_meshes=False,
+ async_loading=False,
+ )
+ try:
+ manager.load_initial_region(-1.0, -1.0)
+ provider = VoxelGridProvider(block_lookup=manager.neighbor_at)
+
+ expected_y = max(generator.get_height(-1, -1), generator.sea_level)
+ assert provider.get_top_solid_block(-1, -1) == expected_y
+
+ manager.update(32.0, -1.0)
+ assert provider.get_block_at(-1, expected_y, -1) is BlockType.AIR
+ assert provider.get_top_solid_block(-1, -1) == -1
+ finally:
+ manager.delete()
+
+
def test_block_bounding_box():
provider = VoxelGridProvider()