1 ¿Qué hace este ejemplo?
Este código muestra cómo:
- Inicializar una ventana y dispositivo DirectX 11 con MantraxGFX.
- Crear un cubo 3D con vértices e índices.
- Configurar matrices de mundo, vista y proyección.
- Dibujar varios cubos reutilizando el mismo mesh.
- Controlar la rotación y visibilidad con el teclado.
2 Requisitos
- Windows + SDK de Windows instalado.
-
MantraxGFX (incluyendo
MantraxGFX.hy libs). - Compilador C++ (Visual Studio recomendado).
-
Shaders HLSL
cube_vs.hlslycube_ps.hlsl.
3 Estructuras: vértices, índices y constantes
3.1. Estructura del vértice
Cada vértice del cubo tiene posición 3D (x, y, z) y color RGB (r, g, b).
struct Vertex {
float x, y, z; // Posición
float r, g, b; // Color
};
3.2. Índices del cubo
El cubo se define con 8 vértices y 36 índices (6 caras × 2 triángulos × 3 vértices).
Vertex vertices[] = {
{-1, -1, -1, 1, 0, 0}, // 0
{ 1, -1, -1, 0, 1, 0}, // 1
{ 1, 1, -1, 0, 0, 1}, // 2
{-1, 1, -1, 1, 1, 0}, // 3
{-1, -1, 1, 1, 0, 1}, // 4
{ 1, -1, 1, 0, 1, 1}, // 5
{ 1, 1, 1, 1, 1, 1}, // 6
{-1, 1, 1, 0, 0, 0} // 7
};
uint32_t indices[] = {
// Frente (+Z)
4, 5, 6, 4, 6, 7,
// Atrás (-Z)
1, 0, 3, 1, 3, 2,
// Izquierda (-X)
0, 4, 7, 0, 7, 3,
// Derecha (+X)
5, 1, 2, 5, 2, 6,
// Arriba (+Y)
7, 6, 2, 7, 2, 3,
// Abajo (-Y)
0, 1, 5, 0, 5, 4
};
3.3. Buffer de constantes: matrices
TransformData agrupa las matrices de mundo, vista y proyección que se enviarán al shader de vértices.
struct TransformData {
float world[16];
float view[16];
float proj[16];
};
4 Inicializar el motor y la ventana
En WinMain se crea una consola para logs, la ventana y el dispositivo gráfico DirectX 11 a través de MantraxGFX.
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
AllocConsole();
FILE* f;
freopen_s(&f, "CONOUT$", "w", stdout);
HWND hwnd = (HWND)MantraxGFX::CreateWindowSimple(
"Mantrax Engine - CUBO 3D",
1280, 720
);
auto gfx = MantraxGFX::CreateDeviceDX11(hwnd, 1280, 720);
// ...
}
- CreateWindowSimple crea la ventana de render.
- CreateDeviceDX11 inicializa el device y el swap chain.
5 Crear buffers y shaders
5.1. Vertex buffer
Almacena la lista de vértices del cubo.
MantraxGFX::BufferDesc vbDesc;
vbDesc.size = sizeof(vertices);
vbDesc.dynamic = false;
vbDesc.type = MantraxGFX::BufferType::Vertex;
auto* vertexBuffer = gfx->CreateBuffer(vbDesc, vertices);
5.2. Index buffer
Indica el orden en que se usan los vértices para formar triángulos.
MantraxGFX::BufferDesc ibDesc;
ibDesc.size = sizeof(indices);
ibDesc.dynamic = false;
ibDesc.type = MantraxGFX::BufferType::Index;
auto* indexBuffer = gfx->CreateBuffer(ibDesc, indices);
5.3. Constant buffer (TransformData)
Se usa para enviar matrices actualizadas cada frame.
MantraxGFX::BufferDesc cbDesc;
cbDesc.size = sizeof(TransformData);
cbDesc.dynamic = true;
cbDesc.type = MantraxGFX::BufferType::Constant;
auto* constantBuffer = gfx->CreateBuffer(cbDesc, nullptr);
5.4. Shaders y pipeline
Se cargan los shaders HLSL y se configura el pipeline (blend/depth).
MantraxGFX::ShaderDesc shaderDesc;
shaderDesc.vsPath = "cube_vs.hlsl";
shaderDesc.psPath = "cube_ps.hlsl";
auto* shader = gfx->CreateShader(shaderDesc);
MantraxGFX::PipelineDesc pipeDesc;
pipeDesc.shader = shader;
pipeDesc.blend = MantraxGFX::BlendMode::Opaque;
pipeDesc.depth = MantraxGFX::DepthMode::Less;
auto* pipeline = gfx->CreatePipeline(pipeDesc);
6 Bucle principal: actualizar matrices y dibujar
En cada frame:
- Se procesa la cola de mensajes de Windows.
- Se calcula
dt(delta time). - Se actualiza la rotación global.
- Se construyen las matrices
world,viewyproj. - Se actualiza el constant buffer.
- Se dibujan los cubos.
// Dentro del while (msg.message != WM_QUIT)
float dt = timer.Tick();
if (g_autoRotate)
g_rotation += dt;
TransformData tdata;
// Matriz de vista (cámara)
MatrixLookAt(
tdata.view,
0.0f, 2.0f, -6.0f, // eye
0.0f, 0.0f, 0.0f // target
);
// Matriz de proyección
uint32_t w, h;
gfx->GetWindowSize(&w, &h);
float aspect = (float)w / (float)h;
MatrixPerspective(
tdata.proj,
3.14159f / 4.0f, // FOV
aspect,
0.1f, // near
100.0f // far
);
auto* cmd = gfx->BeginFrame();
Dibujar varios cubos
En este ejemplo se reutiliza el mismo mesh y se cambia la matriz World para cada cubo.
struct CubeInstance {
float x, y, z;
};
CubeInstance cubes[] = {
{ 0, 0, 0},
{ 3, 0, 0},
{-3, 0, 0}
};
int numCubes = 3; // (ajusta según el tamaño de tu arreglo)
if (g_showCube) {
cmd->SetPipeline(pipeline);
cmd->SetVertexBuffer(vertexBuffer, sizeof(Vertex));
cmd->SetIndexBuffer(indexBuffer);
for (int i = 0; i < numCubes; i++) {
float rot[16], trans[16], world[16];
MatrixRotationY(rot, g_rotation + i * 0.5f);
MatrixTranslation(trans, cubes[i].x, cubes[i].y, cubes[i].z);
// world = rot (copiar)
for (int j = 0; j < 16; j++)
world[j] = rot[j];
// aplicar traslación
world[12] += trans[12];
world[13] += trans[13];
world[14] += trans[14];
memcpy(tdata.world, world, sizeof(world));
gfx->UpdateBuffer(constantBuffer, &tdata, sizeof(tdata));
cmd->SetConstantBuffer(0, constantBuffer);
cmd->DrawIndexed(36);
}
}
gfx->EndFrame();
7 Controles de teclado
Se configura un callback de teclado donde se manejan teclas para controlar la escena.
gfx->SetKeyCallback([](const MantraxGFX::KeyEvent& evt) {
if (evt.pressed && !evt.isRepeat) {
switch (evt.keyCode) {
case VK_ESCAPE:
printf("ESC - salir\n");
PostQuitMessage(0);
break;
case VK_SPACE:
g_showCube = !g_showCube;
printf("SPACE - Cubo %s\n", g_showCube ? "VISIBLE" : "OCULTO");
break;
case VK_LEFT:
g_rotation -= 0.1f;
break;
case VK_RIGHT:
g_rotation += 0.1f;
break;
case 'T':
g_autoRotate = !g_autoRotate;
printf("T - Auto rotación %s\n", g_autoRotate ? "ON" : "OFF");
break;
case 'R':
g_rotation = 0.0f;
printf("R - Reset\n");
break;
}
}
});
Teclas
- ESC – Salir
- SPACE – Mostrar/ocultar cubo(s)
- ← → – Rotación manual
- T – Auto-rotación ON/OFF
- R – Reset rotación
8 Ejemplo de shaders (cube_vs.hlsl / cube_ps.hlsl)
Ejemplo básico de cómo podrían lucir los shaders que esperan el TransformData y el color del vértice:
8.1. Vertex shader (cube_vs.hlsl)
cbuffer TransformBuffer : register(b0)
{
float4x4 world;
float4x4 view;
float4x4 proj;
};
struct VSInput
{
float3 pos : POSITION;
float3 color : COLOR;
};
struct VSOutput
{
float4 pos : SV_POSITION;
float3 color : COLOR;
};
VSOutput main(VSInput input)
{
VSOutput o;
float4 wpos = mul(float4(input.pos, 1.0f), world);
float4 vpos = mul(wpos, view);
float4 ppos = mul(vpos, proj);
o.pos = ppos;
o.color = input.color;
return o;
}
8.2. Pixel shader (cube_ps.hlsl)
struct PSInput
{
float4 pos : SV_POSITION;
float3 color : COLOR;
};
float4 main(PSInput input) : SV_TARGET
{
return float4(input.color, 1.0f);
}