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
45 struct gear
46 {
47 struct mesh* mesh;
48 float x;
49 float y;
50 float t0;
51 float wmul;
52 float4 color;
53 };
54
55 struct 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
66 struct gear gears[3];
67
68 struct vertex
69 {
70 float position[3];
71 float normal[3];
72
vertexvertex73 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
build_gear(ID3D11Device * dev,int triangle_budget,float inner_radius,float outer_radius,float width,int teeth,float tooth_depth)86 static 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
237 struct 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
d3d11gearsd3d11gears268 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
draw_oned3d11gears282 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
get_angled3d11gears301 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
init_for_dimensionsd3d11gears311 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
drawd3d11gears380 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
initd3d11gears477 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
d3d11_application_create()570 d3d11_application* d3d11_application_create()
571 {
572 return new d3d11gears();
573 }
574