1/* 2* Copyright (C) 1999-2001 Brian Paul All Rights Reserved. 3* Copyright (C) 2009-2010 Luca Barbieri All Rights Reserved. 4* 5* Permission is hereby granted, free of charge, to any person obtaining a 6* copy of this software and associated documentation files (the "Software"), 7* to deal in the Software without restriction, including without limitation 8* the rights to use, copy, modify, merge, publish, distribute, sublicense, 9* and/or sell copies of the Software, and to permit persons to whom the 10* Software is furnished to do so, subject to the following conditions: 11*. 12* The above copyright notice and this permission notice shall be included 13* in all copies or substantial portions of the Software. 14* 15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21*/ 22 23/* 24* This is a port of the infamous "glxgears" demo to straight EGL 25* Port by Dane Rushton 10 July 2005 26* 27* This a rewrite of the 'eglgears' demo in straight Gallium 28* Port by Luca Barbieri 29* 30* This a port of the 'galliumgears' demo to Direct3D 11 31* Port by Luca Barbieri 32*/ 33 34#define _USE_MATH_DEFINES 35#include "d3d11app.h" 36#include "d3d11u.h" 37#include "d3d11gears.hlsl.ps.h" 38#include "d3d11gears.hlsl.vs.h" 39 40#include <stdlib.h> 41#include <stdio.h> 42#include <math.h> 43#include <float.h> 44 45struct gear 46{ 47 struct mesh* mesh; 48 float x; 49 float y; 50 float t0; 51 float wmul; 52 float4 color; 53}; 54 55struct cbuf_t 56{ 57 float4x4 projection; 58 float4x4 modelview; 59 float4 light; 60 float4 diffuse; 61 float4 specular; 62 float specular_power; 63 float padding[3]; 64}; 65 66struct gear gears[3]; 67 68struct vertex 69{ 70 float position[3]; 71 float normal[3]; 72 73 vertex(float x, float y, float z, float nx, float ny, float nz) 74 { 75 position[0] = x; 76 position[1] = y; 77 position[2] = z; 78 normal[0] = nx; 79 normal[1] = ny; 80 normal[2] = nz; 81 } 82}; 83 84#define VERT(x, y, z) vertices.push_back(vertex((x), (y), (z), (nx), (ny), (nz))) 85 86static mesh* build_gear(ID3D11Device* dev, int triangle_budget, float inner_radius, float outer_radius, float width, int teeth, float tooth_depth) 87{ 88 int i, j, k; 89 float r0, r1, r2; 90 float da; 91 float nx, ny, nz; 92 int face; 93 int segs = 4; 94 int base_triangles = teeth * segs * 2 * 2; 95 int divs0 = (triangle_budget / base_triangles) - 1; 96 int divs = (divs0 > 0) ? divs0 : 1; 97 float* c = (float*)malloc(teeth * segs * sizeof(float)); 98 float* s = (float*)malloc(teeth * segs * sizeof(float)); 99 float* dc = (float*)malloc(teeth * segs * divs * sizeof(float)); 100 float* ds = (float*)malloc(teeth * segs * divs * sizeof(float)); 101 int num_vertices = teeth * segs * 2 * (3 + 2 * divs); 102 int num_triangles = base_triangles * (1 + divs); 103 printf("Creating gear with %i teeth using %i vertices used in %i triangles\n", teeth, num_vertices, num_triangles); 104 triangle_list_indices<> indices; 105 std::vector<vertex> vertices; 106 107 r0 = inner_radius; 108 r1 = outer_radius - tooth_depth / 2.0f; 109 r2 = outer_radius + tooth_depth / 2.0f; 110 111 da = (float)(2.0 * M_PI / (teeth * segs * divs)); 112 for(i = 0; i < teeth * segs * divs; ++i) { 113 float angle = da * i; 114 ds[i] = sin(angle); 115 dc[i] = cos(angle); 116 } 117 118 for(i = 0; i < teeth * segs; ++i) { 119 s[i] = ds[i * divs]; 120 c[i] = dc[i * divs]; 121 } 122 123 /* faces */ 124 for(face = -1; face <= 1; face += 2) { 125 float z = width * face * 0.5f; 126 nx = 0.0f; 127 ny = 0.0f; 128 nz = (float)face; 129 130 indices.flip = face > 0; 131 132 assert(segs == 4); 133 for(i = 0; i < teeth; ++i) { 134 VERT(r1 * c[segs * i], r1 * s[segs * i], z); 135 VERT(r2 * c[segs * i + 1], r2 * s[segs * i + 1], z); 136 VERT(r2 * c[segs * i + 2], r2 * s[segs * i + 2], z); 137 VERT(r1 * c[segs * i + 3], r1 * s[segs * i + 3], z); 138 } 139 140 for(i = 0; i < teeth * segs * divs; ++i) { 141 VERT(r0 * dc[i], r0 * ds[i], z); 142 } 143 144 for(i = 0; i < teeth; ++i) { 145 for(j = i * segs; j < (i + 1) * segs; ++j) { 146 int nextj = j + 1; 147 if(nextj == teeth * segs) 148 nextj = 0; 149 150 for(k = j * divs; k < (j + 1) * divs; ++k) { 151 int nextk = k + 1; 152 if(nextk == teeth * segs * divs) 153 nextk = 0; 154 indices.poly(teeth * segs + k, j, teeth * segs + nextk); 155 } 156 157 indices.poly(teeth * segs + nextj * divs, j, nextj); 158 } 159 } 160 161 indices.base += teeth * segs * (1 + divs); 162 } 163 164 /* teeth faces */ 165 indices.flip = true; 166 float z = width * 0.5f; 167 168 float* coords = (float*)malloc((segs + 1) * 2 * sizeof(float)); 169 nz = 0; 170 for(i = 0; i < teeth; i++) { 171 int next = i + 1; 172 if(next == teeth) 173 next = 0; 174 175 coords[0] = r1 * c[segs * i]; 176 coords[1] = r1 * s[segs * i]; 177 coords[2] = r2 * c[segs * i + 1]; 178 coords[3] = r2 * s[segs * i + 1]; 179 coords[4] = r2 * c[segs * i + 2]; 180 coords[5] = r2 * s[segs * i + 2]; 181 coords[6] = r1 * c[segs * i + 3]; 182 coords[7] = r1 * s[segs * i + 3]; 183 coords[8] = r1 * c[segs * next]; 184 coords[9] = r1 * s[segs * next]; 185 186 for(int j = 0; j < segs; ++j) { 187 float dx = coords[j * 2] - coords[j * 2 + 2]; 188 float dy = coords[j * 2 + 1] - coords[j * 2 + 3]; 189 float len = hypotf(dx, dy); 190 nx = -dy / len; 191 ny = dx / len; 192 VERT(coords[j * 2], coords[j * 2 + 1], z); 193 VERT(coords[j * 2], coords[j * 2 + 1], -z); 194 VERT(coords[j * 2 + 2], coords[j * 2 + 3], z); 195 VERT(coords[j * 2 + 2], coords[j * 2 + 3], -z); 196 197 indices.poly(0, 1, 3, 2); 198 indices.base += 4; 199 } 200 } 201 free(coords); 202 203 /* inner part - simulate a cylinder */ 204 indices.flip = true; 205 for(i = 0; i < teeth * segs * divs; i++) { 206 int next = i + 1; 207 if(next == teeth * segs * divs) 208 next = 0; 209 210 nx = -dc[i]; 211 ny = -ds[i]; 212 VERT(r0 * dc[i], r0 * ds[i], -width * 0.5f); 213 VERT(r0 * dc[i], r0 * ds[i], width * 0.5f); 214 215 indices.poly(i * 2, i * 2 + 1, next * 2 + 1, next * 2); 216 } 217 218 indices.base += teeth * segs * divs * 2; 219 free(c); 220 free(s); 221 free(dc); 222 free(ds); 223 224 D3D11_INPUT_ELEMENT_DESC elements[2] = 225 { 226 {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, 227 {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, 228 }; 229 230 return new mesh(dev, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, 231 elements, 2, 232 g_vs, sizeof(g_vs), 233 &vertices[0], sizeof(vertices[0]), vertices.size(), 234 &indices[0], sizeof(indices[0]), indices.size()); 235} 236 237struct d3d11gears : public d3d11_application 238{ 239 float view_rotx; 240 float view_roty; 241 float view_rotz; 242 int wireframe; 243 int triangles; 244 float speed; 245 float period; 246 unsigned impressions; 247 bool blue_only; 248 249 float last_time; 250 251 int cur_width; 252 int cur_height; 253 254 ID3D11DepthStencilView* zsv; 255 ID3D11RenderTargetView* offscreen_rtv; 256 ID3D11ShaderResourceView* offscreen_srv; 257 258 ID3D11Device* dev; 259 ID3D11BlendState* blend; 260 ID3D11DepthStencilState* zsa; 261 262 ID3D11PixelShader* ps; 263 ID3D11VertexShader* vs; 264 ID3D11Buffer* cb; 265 266 d3d11_blitter* blitter; 267 268 d3d11gears() 269 : cur_width(-1), cur_height(-1), zsv(0), offscreen_rtv(0), offscreen_srv(0) 270 { 271 view_rotx = (float)(M_PI / 9.0); 272 view_roty = (float)(M_PI / 6.0); 273 view_rotz = 0.0f; 274 wireframe = 0; 275 triangles = 3200; 276 speed = 1.0f; 277 period = -1.0f; 278 impressions = 1; 279 blue_only = false; 280 } 281 282 void draw_one(ID3D11DeviceContext* ctx, cbuf_t& cbd, const float4x4& modelview, float angle) 283 { 284 for(unsigned i = blue_only ? 2 : 0; i < 3; ++i) 285 { 286 float4x4 m2 = modelview; 287 m2 = mat_push_translate(m2, gears[i].x, gears[i].y, 0.0f); 288 m2 = mat_push_rotate(m2, 2, angle * gears[i].wmul + gears[i].t0); 289 290 cbd.modelview = m2; 291 cbd.diffuse = gears[i].color; 292 cbd.specular = gears[i].color; 293 cbd.specular_power = 5.0f; 294 295 ctx->UpdateSubresource(cb, 0, 0, &cbd, 0, 0); 296 297 gears[i].mesh->bind_and_draw(ctx); 298 } 299 } 300 301 float get_angle(double time) 302 { 303 // designed so that 1 = original glxgears speed 304 float mod_speed = M_PI * 70.0f / 180.0f * speed; 305 if(period < 0) 306 return (float)(time * mod_speed); 307 else 308 return (float)(cos(time / period) * period * mod_speed); 309 } 310 311 void init_for_dimensions(unsigned width, unsigned height) 312 { 313 if(zsv) 314 zsv->Release(); 315 ID3D11Texture2D* zsbuf; 316 D3D11_TEXTURE2D_DESC zsbufd; 317 memset(&zsbufd, 0, sizeof(zsbufd)); 318 zsbufd.Width = width; 319 zsbufd.Height = height; 320 zsbufd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; 321 zsbufd.ArraySize = 1; 322 zsbufd.MipLevels = 1; 323 zsbufd.SampleDesc.Count = 1; 324 zsbufd.BindFlags = D3D11_BIND_DEPTH_STENCIL; 325 ensure(dev->CreateTexture2D(&zsbufd, 0, &zsbuf)); 326 ensure(dev->CreateDepthStencilView(zsbuf, 0, &zsv)); 327 zsbuf->Release(); 328 329 ID3D11Texture2D* offscreen; 330 if(offscreen_rtv) 331 { 332 offscreen_rtv->Release(); 333 offscreen_srv->Release(); 334 offscreen_rtv = 0; 335 offscreen_srv = 0; 336 } 337 338 if(impressions > 1) 339 { 340 DXGI_FORMAT formats[] = { 341 DXGI_FORMAT_R32G32B32A32_FLOAT, 342 DXGI_FORMAT_R16G16B16A16_UNORM, 343 DXGI_FORMAT_R16G16B16A16_FLOAT, 344 DXGI_FORMAT_R10G10B10A2_UNORM, 345 }; 346 DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; // this won't work well at all 347 unsigned needed_support = D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_BLENDABLE | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE; 348 for(unsigned i = 0; i < sizeof(formats); ++i) 349 { 350 unsigned support; 351 dev->CheckFormatSupport(DXGI_FORMAT_R32G32B32A32_FLOAT, &support); 352 if((support & needed_support) == needed_support) 353 { 354 format = formats[i]; 355 break; 356 } 357 } 358 359 360 D3D11_TEXTURE2D_DESC offscreend; 361 memset(&offscreend, 0, sizeof(offscreend)); 362 offscreend.Width = width; 363 offscreend.Height = height; 364 365 offscreend.Format = format; 366 offscreend.MipLevels = 1; 367 offscreend.ArraySize = 1; 368 offscreend.SampleDesc.Count = 1; 369 offscreend.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; 370 ensure(dev->CreateTexture2D(&offscreend, 0, &offscreen)); 371 ensure(dev->CreateRenderTargetView(offscreen, 0, &offscreen_rtv)); 372 ensure(dev->CreateShaderResourceView(offscreen, 0, &offscreen_srv)); 373 offscreen->Release(); 374 } 375 376 cur_width = width; 377 cur_height = height; 378 } 379 380 void draw(ID3D11DeviceContext* ctx, ID3D11RenderTargetView* rtv, unsigned width, unsigned height, double time) 381 { 382 D3D11_VIEWPORT vp; 383 memset(&vp, 0, sizeof(vp)); 384 vp.Width = (float)width; 385 vp.Height = (float)height; 386 vp.MaxDepth = 1.0f; 387 388 if((int)width != cur_width || (int)height != cur_height) 389 init_for_dimensions(width, height); 390 391 float4 lightpos = vec(5.0f, 5.0f, 10.0f, 0.0f); 392 float black[4] = {0.0, 0.0, 0.0, 0}; 393 394 float4x4 proj; 395 float4x4 m; 396 397 float xr = (float)width / (float)height; 398 float yr = 1.0f; 399 if(xr < 1.0f) { 400 yr /= xr; 401 xr = 1.0f; 402 } 403 proj = mat4x4_frustum(-xr, xr, -yr, yr, 5.0f, 60.0f); 404 405 m = mat4x4_diag(1.0f); 406 m = mat_push_translate(m, 0.0f, 0.0f, -40.0f); 407 m = mat_push_rotate(m, 0, view_rotx); 408 m = mat_push_rotate(m, 1, view_roty); 409 m = mat_push_rotate(m, 2, view_rotz); 410 411 cbuf_t cbd; 412 413 cbd.projection = proj; 414 cbd.light = lightpos; 415 416 float blend_factor[4] = {1.0f / (float)impressions, 1.0f / (float)impressions, 1.0f / (float)impressions, 1.0f / (float)impressions}; 417 418 ID3D11RenderTargetView* render_rtv; 419 if(impressions == 1) 420 render_rtv = rtv; 421 else 422 render_rtv = offscreen_rtv; 423 424 ctx->RSSetViewports(1, &vp); 425 ctx->ClearRenderTargetView(render_rtv, black); 426 427 ctx->PSSetShader(ps, 0, 0); 428 ctx->VSSetShader(vs, 0, 0); 429 430 ctx->PSSetConstantBuffers(0, 1, &cb); 431 ctx->VSSetConstantBuffers(0, 1, &cb); 432 433 if(impressions == 1) 434 { 435 ctx->OMSetBlendState(0, 0, ~0); 436 ctx->OMSetDepthStencilState(0, 0); 437 ctx->OMSetRenderTargets(1, &rtv, zsv); 438 ctx->ClearDepthStencilView(zsv, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0, 0); 439 draw_one(ctx, cbd, m, get_angle(time)); 440 } 441 else 442 { 443 ctx->OMSetBlendState(blend, blend_factor, ~0); 444 445 float time_delta = (float)time - last_time; 446 float time_delta_per_impression = time_delta / impressions; 447 float base_time = last_time + time_delta_per_impression / 2; 448 for(unsigned impression = 0; impression < impressions; ++impression) 449 { 450 float impression_time = base_time + time_delta_per_impression * impression; 451 452 ctx->ClearDepthStencilView(zsv, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0, 0); 453 454 // do early z-pass since we must not write any pixel more than once due to blending 455 for(unsigned pass = 0; pass < 2; ++pass) 456 { 457 if(pass == 0) 458 { 459 ctx->OMSetRenderTargets(0, 0, zsv); 460 ctx->OMSetDepthStencilState(0, 0); 461 } 462 else 463 { 464 ctx->OMSetRenderTargets(1, &render_rtv, zsv); 465 ctx->OMSetDepthStencilState(zsa, 0); 466 } 467 468 draw_one(ctx, cbd, m, get_angle(impression_time)); 469 } 470 } 471 472 blitter->bind_draw_and_unbind(ctx, offscreen_srv, rtv, 0, 0, (float)width, (float)height, false); 473 } 474 last_time = (float)time; 475 } 476 477 bool init(ID3D11Device* dev, int argc, char** argv) 478 { 479 this->dev = dev; 480 481 for(char** p = argv + 1; *p; ++p) { 482 if(!strcmp(*p, "-w")) 483 wireframe = 1; 484 else if(!strcmp(*p, "-b")) 485 blue_only = true; 486 else if(!strcmp(*p, "-t")) 487 triangles = atoi(*++p); 488 else if(!strcmp(*p, "-m")) 489 impressions = (float)atof(*++p); 490 else if(!strcmp(*p, "-p")) 491 period = (float)atof(*++p); 492 else if(!strcmp(*p, "-s")) 493 speed = (float)atof(*++p); 494 else { 495 fprintf(stderr, "Usage: d3d11gears [-v|-w] [-t TRIANGLES]\n"); 496 fprintf(stderr, "d3d11gears is an enhanced port of glxgears to Direct3D 11\n"); 497 fprintf(stderr, "\n"); 498 //fprintf(stderr, "-v\t\tuse per-vertex diffuse-only lighting (classic glxgears look)\n"); 499 fprintf(stderr, "-w\t\twireframe mode\n"); 500 fprintf(stderr, "-t TRIANGLES\ttriangle budget (default is 3200)\n"); 501 fprintf(stderr, "-m IMPRESSIONS\tmotion blur impressions (default is 1)\n"); 502 fprintf(stderr, "-p PERIOD\tspeed reversal period (default is infinite)\n"); 503 fprintf(stderr, "-s SPEED\tgear speed (default is 1.0)\n"); 504 fprintf(stderr, "-b\tonly show blue gear (for faster motion blur)\n"); 505 return false; 506 } 507 } 508 509 ensure(dev->CreatePixelShader(g_ps, sizeof(g_ps), NULL, &ps)); 510 ensure(dev->CreateVertexShader(g_vs, sizeof(g_vs), NULL, &vs)); 511 512 gears[0].color = vec(0.8f, 0.1f, 0.0f, 1.0f); 513 gears[1].color = vec(0.0f, 0.8f, 0.2f, 1.0f); 514 gears[2].color = vec(0.2f, 0.2f, 1.0f, 1.0f); 515 516 gears[0].mesh = build_gear(dev, triangles / 2, 1.0f, 4.0f, 1.0f, 20, 0.7f); 517 gears[1].mesh = build_gear(dev, triangles / 4, 0.5f, 2.0f, 2.0f, 10, 0.7f); 518 gears[2].mesh = build_gear(dev, triangles / 4, 1.3f, 2.0f, 0.5f, 10, 0.7f); 519 520 gears[0].x = -3.0f; 521 gears[0].y = -2.0f; 522 gears[0].wmul = 1.0f; 523 gears[0].t0 = 0.0 * M_PI / 180.0f; 524 525 gears[1].x = 3.1f; 526 gears[1].y = -2.0f; 527 gears[1].wmul = -2.0f; 528 gears[1].t0 = -9.0f * (float)M_PI / 180.0f; 529 530 gears[2].x = -3.1f; 531 gears[2].y = 4.2f; 532 gears[2].wmul = -2.0f; 533 gears[2].t0 = -25.0f * (float)M_PI / 180.0f; 534 535 D3D11_BUFFER_DESC bufferd; 536 memset(&bufferd, 0, sizeof(bufferd)); 537 bufferd.ByteWidth = sizeof(cbuf_t); 538 bufferd.Usage = D3D11_USAGE_DEFAULT; 539 bufferd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 540 ensure(dev->CreateBuffer(&bufferd, 0, &cb)); 541 542 if(impressions > 1) 543 { 544 D3D11_BLEND_DESC blendd; 545 memset(&blendd, 0, sizeof(blendd)); 546 blendd.RenderTarget[0].BlendEnable = TRUE; 547 blendd.RenderTarget[0].BlendOp = blendd.RenderTarget[0].BlendOpAlpha 548 = D3D11_BLEND_OP_ADD; 549 blendd.RenderTarget[0].SrcBlend = blendd.RenderTarget[0].SrcBlendAlpha 550 = D3D11_BLEND_BLEND_FACTOR; 551 blendd.RenderTarget[0].DestBlend = blendd.RenderTarget[0].DestBlendAlpha 552 = D3D11_BLEND_ONE; 553 blendd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; 554 ensure(dev->CreateBlendState(&blendd, &blend)); 555 556 D3D11_DEPTH_STENCIL_DESC zsad; 557 memset(&zsad, 0, sizeof(zsad)); 558 zsad.DepthEnable = TRUE; 559 zsad.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; 560 zsad.DepthFunc = D3D11_COMPARISON_EQUAL; 561 ensure(dev->CreateDepthStencilState(&zsad, &zsa)); 562 563 blitter = new d3d11_blitter(dev); 564 } 565 566 return true; 567 } 568}; 569 570d3d11_application* d3d11_application_create() 571{ 572 return new d3d11gears(); 573} 574