├── .gitignore ├── LICENSE ├── README.md ├── winforms-z-buffer.sln └── winforms-z-buffer ├── App.config ├── Display.Designer.cs ├── Display.cs ├── Display.resx ├── Drawing ├── Camera.cs ├── Drawing.cs ├── Objects │ ├── Cube.cs │ └── Traingle.cs └── ZBuffer.cs ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Utils ├── Extensions.cs ├── Fill.cs ├── Line.cs ├── Media.Media3D │ ├── Matrix3D.cs │ ├── Point3D.cs │ ├── Point4D.cs │ ├── Quaternion.cs │ └── Vector3D.cs └── Utility.cs ├── packages.config └── winforms-z-buffer.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Coverlet is a free, cross platform Code Coverage Tool 145 | coverage*[.json, .xml, .info] 146 | 147 | # Visual Studio code coverage results 148 | *.coverage 149 | *.coveragexml 150 | 151 | # NCrunch 152 | _NCrunch_* 153 | .*crunch*.local.xml 154 | nCrunchTemp_* 155 | 156 | # MightyMoose 157 | *.mm.* 158 | AutoTest.Net/ 159 | 160 | # Web workbench (sass) 161 | .sass-cache/ 162 | 163 | # Installshield output folder 164 | [Ee]xpress/ 165 | 166 | # DocProject is a documentation generator add-in 167 | DocProject/buildhelp/ 168 | DocProject/Help/*.HxT 169 | DocProject/Help/*.HxC 170 | DocProject/Help/*.hhc 171 | DocProject/Help/*.hhk 172 | DocProject/Help/*.hhp 173 | DocProject/Help/Html2 174 | DocProject/Help/html 175 | 176 | # Click-Once directory 177 | publish/ 178 | 179 | # Publish Web Output 180 | *.[Pp]ublish.xml 181 | *.azurePubxml 182 | # Note: Comment the next line if you want to checkin your web deploy settings, 183 | # but database connection strings (with potential passwords) will be unencrypted 184 | *.pubxml 185 | *.publishproj 186 | 187 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 188 | # checkin your Azure Web App publish settings, but sensitive information contained 189 | # in these scripts will be unencrypted 190 | PublishScripts/ 191 | 192 | # NuGet Packages 193 | *.nupkg 194 | # NuGet Symbol Packages 195 | *.snupkg 196 | # The packages folder can be ignored because of Package Restore 197 | **/[Pp]ackages/* 198 | # except build/, which is used as an MSBuild target. 199 | !**/[Pp]ackages/build/ 200 | # Uncomment if necessary however generally it will be regenerated when needed 201 | #!**/[Pp]ackages/repositories.config 202 | # NuGet v3's project.json files produces more ignorable files 203 | *.nuget.props 204 | *.nuget.targets 205 | 206 | # Microsoft Azure Build Output 207 | csx/ 208 | *.build.csdef 209 | 210 | # Microsoft Azure Emulator 211 | ecf/ 212 | rcf/ 213 | 214 | # Windows Store app package directories and files 215 | AppPackages/ 216 | BundleArtifacts/ 217 | Package.StoreAssociation.xml 218 | _pkginfo.txt 219 | *.appx 220 | *.appxbundle 221 | *.appxupload 222 | 223 | # Visual Studio cache files 224 | # files ending in .cache can be ignored 225 | *.[Cc]ache 226 | # but keep track of directories ending in .cache 227 | !?*.[Cc]ache/ 228 | 229 | # Others 230 | ClientBin/ 231 | ~$* 232 | *~ 233 | *.dbmdl 234 | *.dbproj.schemaview 235 | *.jfm 236 | *.pfx 237 | *.publishsettings 238 | orleans.codegen.cs 239 | 240 | # Including strong name files can present a security risk 241 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 242 | #*.snk 243 | 244 | # Since there are multiple workflows, uncomment next line to ignore bower_components 245 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 246 | #bower_components/ 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | ServiceFabricBackup/ 259 | *.rptproj.bak 260 | 261 | # SQL Server files 262 | *.mdf 263 | *.ldf 264 | *.ndf 265 | 266 | # Business Intelligence projects 267 | *.rdl.data 268 | *.bim.layout 269 | *.bim_*.settings 270 | *.rptproj.rsuser 271 | *- [Bb]ackup.rdl 272 | *- [Bb]ackup ([0-9]).rdl 273 | *- [Bb]ackup ([0-9][0-9]).rdl 274 | 275 | # Microsoft Fakes 276 | FakesAssemblies/ 277 | 278 | # GhostDoc plugin setting file 279 | *.GhostDoc.xml 280 | 281 | # Node.js Tools for Visual Studio 282 | .ntvs_analysis.dat 283 | node_modules/ 284 | 285 | # Visual Studio 6 build log 286 | *.plg 287 | 288 | # Visual Studio 6 workspace options file 289 | *.opt 290 | 291 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 292 | *.vbw 293 | 294 | # Visual Studio LightSwitch build output 295 | **/*.HTMLClient/GeneratedArtifacts 296 | **/*.DesktopClient/GeneratedArtifacts 297 | **/*.DesktopClient/ModelManifest.xml 298 | **/*.Server/GeneratedArtifacts 299 | **/*.Server/ModelManifest.xml 300 | _Pvt_Extensions 301 | 302 | # Paket dependency manager 303 | .paket/paket.exe 304 | paket-files/ 305 | 306 | # FAKE - F# Make 307 | .fake/ 308 | 309 | # CodeRush personal settings 310 | .cr/personal 311 | 312 | # Python Tools for Visual Studio (PTVS) 313 | __pycache__/ 314 | *.pyc 315 | 316 | # Cake - Uncomment if you are using it 317 | # tools/** 318 | # !tools/packages.config 319 | 320 | # Tabs Studio 321 | *.tss 322 | 323 | # Telerik's JustMock configuration file 324 | *.jmconfig 325 | 326 | # BizTalk build output 327 | *.btp.cs 328 | *.btm.cs 329 | *.odx.cs 330 | *.xsd.cs 331 | 332 | # OpenCover UI analysis results 333 | OpenCover/ 334 | 335 | # Azure Stream Analytics local run output 336 | ASALocalRun/ 337 | 338 | # MSBuild Binary and Structured Log 339 | *.binlog 340 | 341 | # NVidia Nsight GPU debugger configuration file 342 | *.nvuser 343 | 344 | # MFractors (Xamarin productivity tool) working folder 345 | .mfractor/ 346 | 347 | # Local History for Visual Studio 348 | .localhistory/ 349 | 350 | # BeatPulse healthcheck temp database 351 | healthchecksdb 352 | 353 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 354 | MigrationBackup/ 355 | 356 | # Ionide (cross platform F# VS Code tools) working folder 357 | .ionide/ 358 | 359 | # Fody - auto-generated XML schema 360 | FodyWeavers.xsd# Ignore Visual Studio temporary files, build results, and 361 | ## files generated by popular Visual Studio add-ons. 362 | ## 363 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 364 | 365 | # User-specific files 366 | *.rsuser 367 | *.suo 368 | *.user 369 | *.userosscache 370 | *.sln.docstates 371 | 372 | # User-specific files (MonoDevelop/Xamarin Studio) 373 | *.userprefs 374 | 375 | # Mono auto generated files 376 | mono_crash.* 377 | 378 | # Build results 379 | [Dd]ebug/ 380 | [Dd]ebugPublic/ 381 | [Rr]elease/ 382 | [Rr]eleases/ 383 | x64/ 384 | x86/ 385 | [Ww][Ii][Nn]32/ 386 | [Aa][Rr][Mm]/ 387 | [Aa][Rr][Mm]64/ 388 | bld/ 389 | [Bb]in/ 390 | [Oo]bj/ 391 | [Ll]og/ 392 | [Ll]ogs/ 393 | 394 | # Visual Studio 2015/2017 cache/options directory 395 | .vs/ 396 | # Uncomment if you have tasks that create the project's static files in wwwroot 397 | #wwwroot/ 398 | 399 | # Visual Studio 2017 auto generated files 400 | Generated\ Files/ 401 | 402 | # MSTest test Results 403 | [Tt]est[Rr]esult*/ 404 | [Bb]uild[Ll]og.* 405 | 406 | # NUnit 407 | *.VisualState.xml 408 | TestResult.xml 409 | nunit-*.xml 410 | 411 | # Build Results of an ATL Project 412 | [Dd]ebugPS/ 413 | [Rr]eleasePS/ 414 | dlldata.c 415 | 416 | # Benchmark Results 417 | BenchmarkDotNet.Artifacts/ 418 | 419 | # .NET Core 420 | project.lock.json 421 | project.fragment.lock.json 422 | artifacts/ 423 | 424 | # ASP.NET Scaffolding 425 | ScaffoldingReadMe.txt 426 | 427 | # StyleCop 428 | StyleCopReport.xml 429 | 430 | # Files built by Visual Studio 431 | *_i.c 432 | *_p.c 433 | *_h.h 434 | *.ilk 435 | *.meta 436 | *.obj 437 | *.iobj 438 | *.pch 439 | *.pdb 440 | *.ipdb 441 | *.pgc 442 | *.pgd 443 | *.rsp 444 | *.sbr 445 | *.tlb 446 | *.tli 447 | *.tlh 448 | *.tmp 449 | *.tmp_proj 450 | *_wpftmp.csproj 451 | *.log 452 | *.vspscc 453 | *.vssscc 454 | .builds 455 | *.pidb 456 | *.svclog 457 | *.scc 458 | 459 | # Chutzpah Test files 460 | _Chutzpah* 461 | 462 | # Visual C++ cache files 463 | ipch/ 464 | *.aps 465 | *.ncb 466 | *.opendb 467 | *.opensdf 468 | *.sdf 469 | *.cachefile 470 | *.VC.db 471 | *.VC.VC.opendb 472 | 473 | # Visual Studio profiler 474 | *.psess 475 | *.vsp 476 | *.vspx 477 | *.sap 478 | 479 | # Visual Studio Trace Files 480 | *.e2e 481 | 482 | # TFS 2012 Local Workspace 483 | $tf/ 484 | 485 | # Guidance Automation Toolkit 486 | *.gpState 487 | 488 | # ReSharper is a .NET coding add-in 489 | _ReSharper*/ 490 | *.[Rr]e[Ss]harper 491 | *.DotSettings.user 492 | 493 | # TeamCity is a build add-in 494 | _TeamCity* 495 | 496 | # DotCover is a Code Coverage Tool 497 | *.dotCover 498 | 499 | # AxoCover is a Code Coverage Tool 500 | .axoCover/* 501 | !.axoCover/settings.json 502 | 503 | # Coverlet is a free, cross platform Code Coverage Tool 504 | coverage*[.json, .xml, .info] 505 | 506 | # Visual Studio code coverage results 507 | *.coverage 508 | *.coveragexml 509 | 510 | # NCrunch 511 | _NCrunch_* 512 | .*crunch*.local.xml 513 | nCrunchTemp_* 514 | 515 | # MightyMoose 516 | *.mm.* 517 | AutoTest.Net/ 518 | 519 | # Web workbench (sass) 520 | .sass-cache/ 521 | 522 | # Installshield output folder 523 | [Ee]xpress/ 524 | 525 | # DocProject is a documentation generator add-in 526 | DocProject/buildhelp/ 527 | DocProject/Help/*.HxT 528 | DocProject/Help/*.HxC 529 | DocProject/Help/*.hhc 530 | DocProject/Help/*.hhk 531 | DocProject/Help/*.hhp 532 | DocProject/Help/Html2 533 | DocProject/Help/html 534 | 535 | # Click-Once directory 536 | publish/ 537 | 538 | # Publish Web Output 539 | *.[Pp]ublish.xml 540 | *.azurePubxml 541 | # Note: Comment the next line if you want to checkin your web deploy settings, 542 | # but database connection strings (with potential passwords) will be unencrypted 543 | *.pubxml 544 | *.publishproj 545 | 546 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 547 | # checkin your Azure Web App publish settings, but sensitive information contained 548 | # in these scripts will be unencrypted 549 | PublishScripts/ 550 | 551 | # NuGet Packages 552 | *.nupkg 553 | # NuGet Symbol Packages 554 | *.snupkg 555 | # The packages folder can be ignored because of Package Restore 556 | **/[Pp]ackages/* 557 | # except build/, which is used as an MSBuild target. 558 | !**/[Pp]ackages/build/ 559 | # Uncomment if necessary however generally it will be regenerated when needed 560 | #!**/[Pp]ackages/repositories.config 561 | # NuGet v3's project.json files produces more ignorable files 562 | *.nuget.props 563 | *.nuget.targets 564 | 565 | # Microsoft Azure Build Output 566 | csx/ 567 | *.build.csdef 568 | 569 | # Microsoft Azure Emulator 570 | ecf/ 571 | rcf/ 572 | 573 | # Windows Store app package directories and files 574 | AppPackages/ 575 | BundleArtifacts/ 576 | Package.StoreAssociation.xml 577 | _pkginfo.txt 578 | *.appx 579 | *.appxbundle 580 | *.appxupload 581 | 582 | # Visual Studio cache files 583 | # files ending in .cache can be ignored 584 | *.[Cc]ache 585 | # but keep track of directories ending in .cache 586 | !?*.[Cc]ache/ 587 | 588 | # Others 589 | ClientBin/ 590 | ~$* 591 | *~ 592 | *.dbmdl 593 | *.dbproj.schemaview 594 | *.jfm 595 | *.pfx 596 | *.publishsettings 597 | orleans.codegen.cs 598 | 599 | # Including strong name files can present a security risk 600 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 601 | #*.snk 602 | 603 | # Since there are multiple workflows, uncomment next line to ignore bower_components 604 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 605 | #bower_components/ 606 | 607 | # RIA/Silverlight projects 608 | Generated_Code/ 609 | 610 | # Backup & report files from converting an old project file 611 | # to a newer Visual Studio version. Backup files are not needed, 612 | # because we have git ;-) 613 | _UpgradeReport_Files/ 614 | Backup*/ 615 | UpgradeLog*.XML 616 | UpgradeLog*.htm 617 | ServiceFabricBackup/ 618 | *.rptproj.bak 619 | 620 | # SQL Server files 621 | *.mdf 622 | *.ldf 623 | *.ndf 624 | 625 | # Business Intelligence projects 626 | *.rdl.data 627 | *.bim.layout 628 | *.bim_*.settings 629 | *.rptproj.rsuser 630 | *- [Bb]ackup.rdl 631 | *- [Bb]ackup ([0-9]).rdl 632 | *- [Bb]ackup ([0-9][0-9]).rdl 633 | 634 | # Microsoft Fakes 635 | FakesAssemblies/ 636 | 637 | # GhostDoc plugin setting file 638 | *.GhostDoc.xml 639 | 640 | # Node.js Tools for Visual Studio 641 | .ntvs_analysis.dat 642 | node_modules/ 643 | 644 | # Visual Studio 6 build log 645 | *.plg 646 | 647 | # Visual Studio 6 workspace options file 648 | *.opt 649 | 650 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 651 | *.vbw 652 | 653 | # Visual Studio LightSwitch build output 654 | **/*.HTMLClient/GeneratedArtifacts 655 | **/*.DesktopClient/GeneratedArtifacts 656 | **/*.DesktopClient/ModelManifest.xml 657 | **/*.Server/GeneratedArtifacts 658 | **/*.Server/ModelManifest.xml 659 | _Pvt_Extensions 660 | 661 | # Paket dependency manager 662 | .paket/paket.exe 663 | paket-files/ 664 | 665 | # FAKE - F# Make 666 | .fake/ 667 | 668 | # CodeRush personal settings 669 | .cr/personal 670 | 671 | # Python Tools for Visual Studio (PTVS) 672 | __pycache__/ 673 | *.pyc 674 | 675 | # Cake - Uncomment if you are using it 676 | # tools/** 677 | # !tools/packages.config 678 | 679 | # Tabs Studio 680 | *.tss 681 | 682 | # Telerik's JustMock configuration file 683 | *.jmconfig 684 | 685 | # BizTalk build output 686 | *.btp.cs 687 | *.btm.cs 688 | *.odx.cs 689 | *.xsd.cs 690 | 691 | # OpenCover UI analysis results 692 | OpenCover/ 693 | 694 | # Azure Stream Analytics local run output 695 | ASALocalRun/ 696 | 697 | # MSBuild Binary and Structured Log 698 | *.binlog 699 | 700 | # NVidia Nsight GPU debugger configuration file 701 | *.nvuser 702 | 703 | # MFractors (Xamarin productivity tool) working folder 704 | .mfractor/ 705 | 706 | # Local History for Visual Studio 707 | .localhistory/ 708 | 709 | # BeatPulse healthcheck temp database 710 | healthchecksdb 711 | 712 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 713 | MigrationBackup/ 714 | 715 | # Ionide (cross platform F# VS Code tools) working folder 716 | .ionide/ 717 | 718 | # Fody - auto-generated XML schema 719 | FodyWeavers.xsd 720 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Julian Szachowicz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tackling the z-buffer algorithm with C# 2 | 3 | ## Introduction 4 | 5 | This project uses C# (and winforms) to present a basic approach to the z-buffer rendering algorithm ([wikipedia](https://en.wikipedia.org/wiki/Z-buffering)) for objects in 3D space. 6 | 7 | The application allows stacking of various objects 8 | 9 |

10 | 11 |

12 | 13 | And moving objects around the scene 14 | 15 |

16 | 17 |

18 | 19 | ## Application Overview 20 | 21 | The winforms main window is `Display.cs`. Cubes are added and defined in `initializeCubes()`. A cube is added in the following way: 22 | 23 | ```csharp 24 | Cube c3 = Cube.UnitCube(1337); // Create a unit cube with color seed (int) 25 | c3.Rescale(2, 5, 15); // Rescale on x, y and z dimensions 26 | c3.Translate(new Vector3D(0, -15, 0)); // Set offset 27 | c3.RotateAroundOrigin(-Math.PI / 3, Math.PI / 8, -Math.PI / 2); // Rotate with respect to/anchored in Origin point (0, 0, 0) 28 | cubes.Add(c3); // Add the cube to list of cubes 29 | ``` 30 | 31 | `Drawing.cs` is the master class used for z-buffer rendering and `ZBuffer.cs` is used to store z-indicies of the projected points. Points are drawn to a bitmap with an extension method which directly writes to memory for better performance. 32 | 33 | ## Control Set 34 | 35 | The below set of combinations of keys and functional keys (shift/ctrl) are used to control the simulation. 36 | 37 | Where applicable (in Note column), keys may be modified with the Ctrl key to increase the value of action (ie. ` + Ctrl` will perfrom the appropriate action with higher value - translation by 10 points instead of 2, etc.) 38 | 39 | Objects are always moved with respect to their local axis. 40 | 41 | **Screen/Scene Controls** 42 | | Key(s) | Action | Note | 43 | | :--- | :--- | :--- | 44 | | R | Refresh screen. | This action is called by default after every other action | 45 | | R + Ctrl | Reset scene to initial view. | - | 46 | | O | Decreases camera FOV by pi/12 | - | 47 | | P | Increases camera FOV by pi/12 | - | 48 | | 1 - 9 | Select the nth object in scene (numbered by programatic order - added to list) | Maximum of 9 objects are controllable with keys | 49 | | 0 | Select all objects | - | 50 | 51 | **Translation Controls** 52 | | Key(s) | Action | Note | 53 | | :--- | :--- | :--- | 54 | | Right arrow | Translates selected objects towards negative X | May be increased with Ctrl | 55 | | Left arrow | Translates selected objects towards positive X | May be increased with Ctrl | 56 | | Up arrow | Translates selected objects towards negative Y | May be increased with Ctrl | 57 | | Down arrow | Translates selected objects towards positive Y | May be increased with Ctrl | 58 | | Z | Translates selected objects towards positive Z | May be increased with Ctrl | 59 | | X | Translates selected objects towards negative Z | May be increased with Ctrl | 60 | 61 | **Rotation Controls** 62 | | Key(s) | Action | Note | 63 | | :--- | :--- | :--- | 64 | | A | Rotates selected objects around positive origin Y | May be increased with Ctrl | 65 | | A + Shift | Rotates selected objects around positive local Y | May be increased with Ctrl | 66 | | D | Rotates selected objects around negative origin Y | May be increased with Ctrl | 67 | | D + Shift | Rotates selected objects around negative local Y | May be increased with Ctrl | 68 | | Q | Rotates selected objects around positive origin Z | May be increased with Ctrl | 69 | | Q + Shift | Rotates selected objects around positive local Z | May be increased with Ctrl | 70 | | E | Rotates selected objects around negative origin Z | May be increased with Ctrl | 71 | | E + Shift | Rotates selected objects around negative local Z | May be increased with Ctrl | 72 | | W | Rotates selected objects around negative origin X | May be increased with Ctrl | 73 | | W + Shift | Rotates selected objects around negative local X | May be increased with Ctrl | 74 | | S | Rotates selected objects around positive origin X | May be increased with Ctrl | 75 | | S + Shift | Rotates selected objects around positive local X | May be increased with Ctrl | 76 | 77 | **Scale Controls** 78 | | Key(s) | Action | Note | 79 | | :--- | :--- | --- | 80 | | . | Upscales selected objects | May be increased with Ctrl | 81 | | , | Downscales selected objects | May be increased with Ctrl | 82 | -------------------------------------------------------------------------------- /winforms-z-buffer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winforms-z-buffer", "winforms-z-buffer\winforms-z-buffer.csproj", "{80C2B2E4-5216-4E26-9725-CDCA55DE81D8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {80C2B2E4-5216-4E26-9725-CDCA55DE81D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {80C2B2E4-5216-4E26-9725-CDCA55DE81D8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {80C2B2E4-5216-4E26-9725-CDCA55DE81D8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {80C2B2E4-5216-4E26-9725-CDCA55DE81D8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {202C555C-2C92-4C52-B737-4FADC26BADC1} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /winforms-z-buffer/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /winforms-z-buffer/Display.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace winforms_z_buffer 2 | { 3 | partial class Display 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.SuspendLayout(); 32 | // 33 | // Display 34 | // 35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 37 | this.ClientSize = new System.Drawing.Size(800, 450); 38 | this.Name = "Display"; 39 | this.Text = "Form1"; 40 | this.ResumeLayout(false); 41 | 42 | } 43 | 44 | #endregion 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /winforms-z-buffer/Display.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | using System.Windows.Media.Media3D; 6 | 7 | namespace winforms_z_buffer 8 | { 9 | public partial class Display : Form 10 | { 11 | List cubes = new List(); 12 | List selectedCubes = new List(); 13 | 14 | PictureBox pb; 15 | 16 | public Display() 17 | { 18 | initializeForm(); 19 | 20 | initializePicturebox(); 21 | 22 | initializeCamera(); 23 | 24 | initializeCubes(); 25 | 26 | OnPaint(null); 27 | } 28 | 29 | void initializeForm() 30 | { 31 | Width = Height = 800; 32 | StartPosition = FormStartPosition.CenterScreen; 33 | 34 | SetStyle( 35 | ControlStyles.AllPaintingInWmPaint | 36 | ControlStyles.UserPaint | 37 | ControlStyles.DoubleBuffer, 38 | true); 39 | } 40 | 41 | void initializePicturebox() 42 | { 43 | pb = new PictureBox 44 | { 45 | Name = "pb", 46 | Size = Size, 47 | Location = new Point(0, 0), 48 | Dock = DockStyle.Fill 49 | }; 50 | Controls.Add(pb); 51 | } 52 | 53 | void initializeCamera() 54 | { 55 | new Camera(/* fov, near, far, aspect ratio */); 56 | } 57 | 58 | void initializeCubes() 59 | { 60 | cubes.Clear(); 61 | 62 | //Cubes 63 | Cube c1 = Cube.UnitCube(42); 64 | c1.Rescale(5, 2, 2); 65 | c1.RotateAroundLocalAxis(0, -Math.PI / 3, -Math.PI / 9); 66 | c1.Translate(new Vector3D(10, 10, -3)); 67 | cubes.Add(c1); 68 | 69 | Cube c2 = Cube.UnitCube(2137); 70 | c2.Rescale(5, 5, 5); 71 | c2.RotateAroundLocalAxis(0, Math.PI / 4, Math.PI / 12); 72 | c2.Translate(new Vector3D(-10, 5, -2)); 73 | cubes.Add(c2); 74 | 75 | Cube c3 = Cube.UnitCube(1337); 76 | c3.Rescale(2, 5, 15); 77 | c3.Translate(new Vector3D(0, -15, 0)); 78 | c3.RotateAroundOrigin(-Math.PI / 3, Math.PI / 8, -Math.PI / 2); 79 | cubes.Add(c3); 80 | 81 | Cube c4 = Cube.UnitCube(420); 82 | c4.Rescale(3, 3, 3); 83 | c4.Translate(new Vector3D(-15, -15, -15)); 84 | c4.RotateAroundOrigin(Math.PI / 7, 0, Math.PI / 64); 85 | cubes.Add(c4); 86 | 87 | Cube c5 = Cube.UnitCube(0); 88 | c5.Rescale(0.5, 0.5, 20); 89 | c5.Translate(new Vector3D(0, 15, -15)); 90 | c5.RotateAroundLocalAxis(Math.PI / 7, 0, Math.PI / 64); 91 | cubes.Add(c5); 92 | 93 | selectedCubes.AddRange(cubes); 94 | } 95 | 96 | protected override void OnPaint(PaintEventArgs args) 97 | { 98 | Bitmap bmp = new Bitmap(Width, Height); 99 | var gr = Graphics.FromImage(bmp); 100 | gr.Clear(Color.Black); 101 | 102 | var drawing = new Drawing(bmp, Size, /*draw faces*/true); 103 | 104 | foreach (var c in cubes) 105 | drawing.Draw(c); 106 | 107 | pb.Image = drawing.GetResult(); 108 | } 109 | 110 | #region shapes control 111 | 112 | void TranslateCubes(Vector3D displacement) 113 | { 114 | foreach (var c in selectedCubes) 115 | c.Translate(displacement); 116 | } 117 | void ScaleCubes(double factor) 118 | { 119 | foreach (var c in selectedCubes) 120 | c.Rescale(factor, factor, factor); 121 | } 122 | void RotateCubesAroundOrigin(double angleX, double angleY, double angleZ) 123 | { 124 | foreach (var c in selectedCubes) 125 | c.RotateAroundOrigin(angleX, angleY, angleZ); 126 | } 127 | void RotateCubesAroundLocal(double angleX, double angleY, double angleZ) 128 | { 129 | foreach (var c in selectedCubes) 130 | c.RotateAroundLocalAxis(angleX, angleY, angleZ); 131 | } 132 | 133 | void ChangeSelectedCubes(int index) 134 | { 135 | selectedCubes.Clear(); 136 | 137 | if (index == -1) 138 | selectedCubes.AddRange(cubes); 139 | else if (index > -1 && index < cubes.Count) 140 | selectedCubes.Add(cubes[index]); 141 | } 142 | protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 143 | { 144 | double standardSpeed = 2; 145 | double fastSpeed = 10; 146 | double rotationStep = Math.PI / 64; 147 | 148 | switch (keyData) 149 | { 150 | case Keys.R: 151 | OnPaint(null); 152 | break; 153 | case (Keys.R | Keys.Control): 154 | initializeCubes(); 155 | initializeCamera(); 156 | goto case Keys.R; 157 | 158 | // Translation 159 | case Keys.Right: 160 | TranslateCubes(new Vector3D(-standardSpeed, 0, 0)); 161 | goto case Keys.R; 162 | case (Keys.Right | Keys.Control): 163 | TranslateCubes(new Vector3D(-fastSpeed, 0, 0)); 164 | goto case Keys.R; 165 | case Keys.Left: 166 | TranslateCubes(new Vector3D(standardSpeed, 0, 0)); 167 | goto case Keys.R; 168 | case (Keys.Left | Keys.Control): 169 | TranslateCubes(new Vector3D(fastSpeed, 0, 0)); 170 | goto case Keys.R; 171 | 172 | case Keys.Up: 173 | TranslateCubes(new Vector3D(0, standardSpeed, 0)); 174 | goto case Keys.R; 175 | case (Keys.Up | Keys.Control): 176 | TranslateCubes(new Vector3D(0, fastSpeed, 0)); 177 | goto case Keys.R; 178 | case Keys.Down: 179 | TranslateCubes(new Vector3D(0, -standardSpeed, 0)); 180 | goto case Keys.R; 181 | case (Keys.Down | Keys.Control): 182 | TranslateCubes(new Vector3D(0, -fastSpeed, 0)); 183 | goto case Keys.R; 184 | 185 | case Keys.Z: 186 | TranslateCubes(new Vector3D(0, 0, standardSpeed)); 187 | goto case Keys.R; 188 | case (Keys.Z | Keys.Control): 189 | TranslateCubes(new Vector3D(0, 0, fastSpeed)); 190 | goto case Keys.R; 191 | case Keys.X: 192 | TranslateCubes(new Vector3D(0, 0, -standardSpeed)); 193 | goto case Keys.R; 194 | case (Keys.X | Keys.Control): 195 | TranslateCubes(new Vector3D(0, 0, -fastSpeed)); 196 | goto case Keys.R; 197 | 198 | // Rotation 199 | case Keys.A: 200 | RotateCubesAroundOrigin(0, standardSpeed * rotationStep, 0); 201 | goto case Keys.R; 202 | case (Keys.A | Keys.Control): 203 | RotateCubesAroundOrigin(0, fastSpeed * rotationStep, 0); 204 | goto case Keys.R; 205 | case Keys.D: 206 | RotateCubesAroundOrigin(0, -standardSpeed * rotationStep, 0); 207 | goto case Keys.R; 208 | case (Keys.D | Keys.Control): 209 | RotateCubesAroundOrigin(0, -fastSpeed * rotationStep, 0); 210 | goto case Keys.R; 211 | 212 | case Keys.Q: 213 | RotateCubesAroundOrigin(0, 0, standardSpeed * rotationStep); 214 | goto case Keys.R; 215 | case (Keys.Q | Keys.Control): 216 | RotateCubesAroundOrigin(0, 0, fastSpeed * rotationStep); 217 | goto case Keys.R; 218 | case Keys.E: 219 | RotateCubesAroundOrigin(0, 0, -standardSpeed * rotationStep); 220 | goto case Keys.R; 221 | case (Keys.E | Keys.Control): 222 | RotateCubesAroundOrigin(0, 0, -fastSpeed * rotationStep); 223 | goto case Keys.R; 224 | 225 | case Keys.S: 226 | RotateCubesAroundOrigin(standardSpeed * rotationStep, 0, 0); 227 | goto case Keys.R; 228 | case (Keys.S | Keys.Control): 229 | RotateCubesAroundOrigin(fastSpeed * rotationStep, 0, 0); 230 | goto case Keys.R; 231 | case Keys.W: 232 | RotateCubesAroundOrigin(-standardSpeed * rotationStep, 0, 0); 233 | goto case Keys.R; 234 | case (Keys.W | Keys.Control): 235 | RotateCubesAroundOrigin(-fastSpeed * rotationStep, 0, 0); 236 | goto case Keys.R; 237 | 238 | case (Keys.A | Keys.Shift): 239 | RotateCubesAroundLocal(0, standardSpeed * rotationStep, 0); 240 | goto case Keys.R; 241 | case (Keys.A | Keys.Control | Keys.Shift): 242 | RotateCubesAroundLocal(0, fastSpeed * rotationStep, 0); 243 | goto case Keys.R; 244 | case (Keys.D | Keys.Shift): 245 | RotateCubesAroundLocal(0, -standardSpeed * rotationStep, 0); 246 | goto case Keys.R; 247 | case (Keys.D | Keys.Control | Keys.Shift): 248 | RotateCubesAroundLocal(0, -fastSpeed * rotationStep, 0); 249 | goto case Keys.R; 250 | 251 | case (Keys.Q | Keys.Shift): 252 | RotateCubesAroundLocal(0, 0, standardSpeed * rotationStep); 253 | goto case Keys.R; 254 | case (Keys.Q | Keys.Control | Keys.Shift): 255 | RotateCubesAroundLocal(0, 0, fastSpeed * rotationStep); 256 | goto case Keys.R; 257 | case (Keys.E | Keys.Shift): 258 | RotateCubesAroundLocal(0, 0, -standardSpeed * rotationStep); 259 | goto case Keys.R; 260 | case (Keys.E | Keys.Control | Keys.Shift): 261 | RotateCubesAroundLocal(0, 0, -fastSpeed * rotationStep); 262 | goto case Keys.R; 263 | 264 | case (Keys.S | Keys.Shift): 265 | RotateCubesAroundLocal(standardSpeed * rotationStep, 0, 0); 266 | goto case Keys.R; 267 | case (Keys.S | Keys.Control | Keys.Shift): 268 | RotateCubesAroundLocal(fastSpeed * rotationStep, 0, 0); 269 | goto case Keys.R; 270 | case (Keys.W | Keys.Shift): 271 | RotateCubesAroundLocal(-standardSpeed * rotationStep, 0, 0); 272 | goto case Keys.R; 273 | case (Keys.W | Keys.Control | Keys.Shift): 274 | RotateCubesAroundLocal(-fastSpeed * rotationStep, 0, 0); 275 | goto case Keys.R; 276 | 277 | // Scale 278 | case Keys.OemPeriod: 279 | ScaleCubes(1.5); 280 | goto case Keys.R; 281 | case (Keys.OemPeriod | Keys.Control): 282 | ScaleCubes(2); 283 | goto case Keys.R; 284 | case Keys.Oemcomma: 285 | ScaleCubes(0.75); 286 | goto case Keys.R; 287 | case (Keys.Oemcomma | Keys.Control): 288 | ScaleCubes(0.5); 289 | goto case Keys.R; 290 | 291 | // Camera 292 | case Keys.O: 293 | Camera.Instance.ChangeFOV(Math.PI / 12); 294 | goto case Keys.R; 295 | case Keys.P: 296 | Camera.Instance.ChangeFOV(-Math.PI / 12); 297 | goto case Keys.R; 298 | 299 | // Cube selection 300 | case Keys.D1: 301 | ChangeSelectedCubes(0); 302 | goto case Keys.R; 303 | case Keys.D2: 304 | ChangeSelectedCubes(1); 305 | goto case Keys.R; 306 | case Keys.D3: 307 | ChangeSelectedCubes(2); 308 | goto case Keys.R; 309 | case Keys.D4: 310 | ChangeSelectedCubes(3); 311 | goto case Keys.R; 312 | case Keys.D5: 313 | ChangeSelectedCubes(4); 314 | goto case Keys.R; 315 | case Keys.D6: 316 | ChangeSelectedCubes(5); 317 | goto case Keys.R; 318 | case Keys.D7: 319 | ChangeSelectedCubes(6); 320 | goto case Keys.R; 321 | case Keys.D8: 322 | ChangeSelectedCubes(7); 323 | goto case Keys.R; 324 | case Keys.D9: 325 | ChangeSelectedCubes(8); 326 | goto case Keys.R; 327 | case Keys.D0: 328 | ChangeSelectedCubes(-1); 329 | goto case Keys.R; 330 | } 331 | 332 | return base.ProcessCmdKey(ref msg, keyData); 333 | } 334 | 335 | #endregion 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /winforms-z-buffer/Display.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /winforms-z-buffer/Drawing/Camera.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media.Media3D; 7 | 8 | namespace winforms_z_buffer 9 | { 10 | public class Camera 11 | { 12 | public static Camera Instance; 13 | 14 | double fov = Math.PI / 2; 15 | double near = 0.001; 16 | double far = 10000; 17 | double aspect = 1; 18 | 19 | public Camera(double fov, double near, double far) : this() 20 | { 21 | this.fov = fov; 22 | this.near = near; 23 | this.far = far; 24 | } 25 | 26 | public Camera() { Instance = this; } 27 | 28 | public Matrix3D GetProjectionMatrix() 29 | { 30 | double fn = far - near; 31 | double fovRad = 1 / Math.Tan(fov / 2); 32 | 33 | Matrix3D matrix = new Matrix3D( 34 | aspect * fovRad, 0, 0, 0, 35 | 0, fovRad, 0, 0, 36 | 0, 0, far / fn, -far * near / fn, 37 | 0, 0, 1, 0 38 | ); 39 | 40 | return matrix; 41 | } 42 | 43 | public void ChangeFOV(double displacement) 44 | { 45 | fov += displacement; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /winforms-z-buffer/Drawing/Drawing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Media.Media3D; 9 | using winforms_z_buffer.Utils; 10 | 11 | namespace winforms_z_buffer 12 | { 13 | public class Drawing 14 | { 15 | ZBuffer zBuffer; 16 | Bitmap bmp; 17 | 18 | Size w; 19 | bool drawFill; 20 | 21 | public Drawing(Bitmap bmp, Size windowSize, bool drawFill) 22 | { 23 | this.bmp = bmp; 24 | zBuffer = new ZBuffer(windowSize); 25 | this.drawFill = drawFill; 26 | w = windowSize; 27 | } 28 | 29 | public Bitmap GetResult() { return bmp; } 30 | 31 | public void Draw(Cube c) 32 | { 33 | foreach (var side in c.Sides) 34 | foreach (var triangle in side) 35 | DrawTriangle(triangle); 36 | } 37 | 38 | public void DrawTriangle(Triangle t) 39 | { 40 | if (!drawFill) 41 | return; 42 | 43 | var points = t.TriangleVertexScreenPointsAsPoint3D(); 44 | 45 | drawTriangleFill(points, t.FaceColor); 46 | } 47 | 48 | void drawTriangleFill(Point3D[] vertices, Color color) 49 | { 50 | var points = new List { vertices[0], vertices[1], vertices[2] }; 51 | 52 | foreach (var p in Fill.FillTriangle(points)) 53 | drawPoint(p, color); 54 | } 55 | 56 | void drawPoint(Point3D point, Color color) 57 | { 58 | var p2D = (Point)point; 59 | 60 | if (zBuffer[point.X, point.Y] <= point.Z) 61 | return; 62 | 63 | zBuffer[point.X, point.Y] = point.Z; 64 | 65 | bmp.SetPixelFast(p2D.X + w.Width / 2, p2D.Y + w.Height / 2, color); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /winforms-z-buffer/Drawing/Objects/Cube.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Drawing2D; 5 | using System.Linq; 6 | using System.Runtime.CompilerServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms.VisualStyles; 10 | using System.Windows.Media.Media3D; 11 | using System.Xml.Linq; 12 | 13 | namespace winforms_z_buffer 14 | { 15 | public class Cube 16 | { 17 | public Color[] Colors = new Color[6]; 18 | 19 | public Color EdgeColor; 20 | public Color FaceColor; 21 | 22 | public Point3D[] Vertices; // Model Vertices 23 | 24 | #region model to world operations 25 | 26 | Vector3D translationVector = new Vector3D(0, 0, 0); 27 | Matrix3D translation 28 | { 29 | get 30 | { 31 | Matrix3D m = new Matrix3D( 32 | 1, 0, 0, 0, 33 | 0, 1, 0, 0, 34 | 0, 0, 1, 0, 35 | translationVector.X, translationVector.Y, translationVector.Z, 1 36 | ); 37 | return m; 38 | } 39 | } 40 | 41 | Vector3D rotationOVector = new Vector3D(0, 0, 0); 42 | Matrix3D rotationO 43 | { 44 | get 45 | { 46 | return getRotationMatrix(rotationOVector); 47 | } 48 | } 49 | 50 | Vector3D rotationLVector = new Vector3D(0, 0, 0); 51 | Matrix3D rotationL 52 | { 53 | get 54 | { 55 | return getRotationMatrix(rotationLVector); 56 | } 57 | } 58 | 59 | Vector3D scaleVector = new Vector3D(1, 1, 1); 60 | Matrix3D scale 61 | { 62 | get 63 | { 64 | return new Matrix3D( 65 | scaleVector.X, 0, 0, 0, 66 | 0, scaleVector.Y, 0, 0, 67 | 0, 0, scaleVector.Z, 0, 68 | 0, 0, 0, 1 69 | ); 70 | } 71 | } 72 | 73 | public Matrix3D Model 74 | { 75 | get 76 | { 77 | return scale * rotationL * translation * rotationO; 78 | } 79 | } 80 | 81 | Matrix3D getRotationMatrix(Vector3D v) 82 | { 83 | Matrix3D rX = new Matrix3D( 84 | 1, 0, 0, 0, 85 | 0, Math.Cos(v.X), -Math.Sin(v.X), 0, 86 | 0, Math.Sin(v.X), Math.Cos(v.X), 0, 87 | 0, 0, 0, 1 88 | ); 89 | Matrix3D rY = new Matrix3D( 90 | Math.Cos(v.Y), 0, Math.Sin(v.Y), 0, 91 | 0, 1, 0, 0, 92 | -Math.Sin(v.Y), 0, Math.Cos(v.Y), 0, 93 | 0, 0, 0, 1 94 | ); 95 | Matrix3D rZ = new Matrix3D( 96 | Math.Cos(v.Z), -Math.Sin(v.Z), 0, 0, 97 | Math.Sin(v.Z), Math.Cos(v.Z), 0, 0, 98 | 0, 0, 1, 0, 99 | 0, 0, 0, 1 100 | ); 101 | 102 | return rX * rY * rZ; 103 | } 104 | 105 | Point3D[] modelToWorld() 106 | { 107 | Point3D[] Vertices = new Point3D[this.Vertices.Length]; 108 | for (int i = 0; i < this.Vertices.Length; i++) 109 | { 110 | Vertices[i] = this.Vertices[i]; 111 | Model.MultiplyPoint(ref Vertices[i]); 112 | } 113 | return Vertices; 114 | } 115 | 116 | #endregion 117 | 118 | #region triangles 119 | 120 | public Triangle[][] Sides 121 | { 122 | get 123 | { 124 | Point3D[] Vertices = modelToWorld(); 125 | 126 | var sides = new Triangle[][] 127 | { 128 | new Triangle[] 129 | { 130 | new Triangle(Vertices[0], Vertices[1], Vertices[2], EdgeColor, Colors[0]), 131 | new Triangle(Vertices[1], Vertices[2], Vertices[3], EdgeColor, Colors[0]) 132 | }, 133 | new Triangle[] 134 | { 135 | new Triangle(Vertices[4], Vertices[5], Vertices[6], EdgeColor, Colors[1]), 136 | new Triangle(Vertices[5], Vertices[6], Vertices[7], EdgeColor, Colors[1]) 137 | }, 138 | new Triangle[] 139 | { 140 | new Triangle(Vertices[0], Vertices[4], Vertices[1], EdgeColor, Colors[2]), 141 | new Triangle(Vertices[5], Vertices[4], Vertices[1], EdgeColor, Colors[2]) 142 | }, 143 | new Triangle[] 144 | { 145 | new Triangle(Vertices[3], Vertices[5], Vertices[7], EdgeColor, Colors[3]), 146 | new Triangle(Vertices[3], Vertices[5], Vertices[1], EdgeColor, Colors[3]) 147 | }, 148 | new Triangle[] 149 | { 150 | new Triangle(Vertices[2], Vertices[6], Vertices[3], EdgeColor, Colors[4]), 151 | new Triangle(Vertices[7], Vertices[6], Vertices[3], EdgeColor, Colors[4]) 152 | }, 153 | new Triangle[] 154 | { 155 | new Triangle(Vertices[2], Vertices[6], Vertices[4], EdgeColor, Colors[5]), 156 | new Triangle(Vertices[2], Vertices[0], Vertices[4], EdgeColor, Colors[5]) 157 | }, 158 | }; 159 | return sides; 160 | } 161 | } 162 | 163 | #endregion 164 | 165 | #region statics & contructor 166 | 167 | public Cube(Point3D[] vertices, int colorSeed) 168 | { 169 | Vertices = vertices; 170 | 171 | Random rnd = new Random(colorSeed); 172 | 173 | for (int i = 0; i < 6; i++) 174 | Colors[i] = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256)); 175 | } 176 | 177 | public static Cube UnitCube(int colorSeed) 178 | { 179 | Point3D[] vertices = { 180 | new Point3D(-1, -1, -1), new Point3D(-1, -1, 1), new Point3D(-1, 1, -1), 181 | new Point3D(-1, 1, 1), new Point3D(1, -1, -1), new Point3D(1, -1, 1), 182 | new Point3D(1, 1, -1), new Point3D(1, 1, 1) 183 | }; 184 | 185 | return new Cube(vertices, colorSeed); 186 | } 187 | 188 | public static int[][] UnitCubeEdgePairs() 189 | { 190 | return new int[][] 191 | { 192 | new int[] {0, 1}, new int[] {1, 3}, new int[] {3, 2}, new int[] {2, 0}, 193 | new int[] {4, 5}, new int[] {5, 7}, new int[] {7, 6}, new int[] {6, 4}, 194 | new int[] {0, 4}, new int[] {1, 5}, 195 | new int[] {2, 6}, new int[] {3, 7} 196 | }; 197 | } 198 | 199 | #endregion 200 | 201 | #region methods-properties 202 | 203 | public void Translate(Vector3D displacement) 204 | { 205 | translationVector += displacement; 206 | } 207 | 208 | public void RotateAroundOrigin(double angleX, double angleY, double angleZ) 209 | { 210 | rotationOVector.X += angleX; 211 | rotationOVector.Y += angleY; 212 | rotationOVector.Z += angleZ; 213 | } 214 | 215 | public void RotateAroundLocalAxis(double angleX, double angleY, double angleZ) 216 | { 217 | rotationLVector.X += angleX; 218 | rotationLVector.Y += angleY; 219 | rotationLVector.Z += angleZ; 220 | } 221 | 222 | public void Rescale(double factorX, double factorY, double factorZ) 223 | { 224 | scaleVector = new Vector3D(factorX * scaleVector.X, factorY * scaleVector.Y, factorZ * scaleVector.Z); 225 | } 226 | 227 | #endregion 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /winforms-z-buffer/Drawing/Objects/Traingle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using System.Windows.Media.Media3D; 9 | using winforms_z_buffer.Utils; 10 | 11 | namespace winforms_z_buffer 12 | { 13 | public class Triangle 14 | { 15 | public Point3D[] Vertices; 16 | public Color EdgeColor; 17 | public Color FaceColor; 18 | 19 | public Triangle(Point3D v1, Point3D v2, Point3D v3, Color eColor, Color fColor) 20 | { 21 | Vertices = new Point3D[3] { v1, v2, v3 }; 22 | EdgeColor = eColor; 23 | FaceColor = fColor; 24 | } 25 | 26 | public void Translate(Vector3D displacement) 27 | { 28 | Vertices[0] += displacement; 29 | Vertices[1] += displacement; 30 | Vertices[2] += displacement; 31 | } 32 | 33 | Point3D[] getPerspective(Point3D[] set) 34 | { 35 | var points = new Point3D[set.Length]; 36 | 37 | foreach (var (v, i) in set.WithIndex()) 38 | { 39 | points[i] = v; 40 | 41 | points[i].Z += 100; 42 | 43 | Camera.Instance.GetProjectionMatrix().MultiplyPoint(ref points[i]); 44 | } 45 | 46 | return points; 47 | } 48 | 49 | public Point3D[] TriangleVertexScreenPointsAsPoint3D() 50 | { 51 | return getPerspective(Vertices); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Drawing/ZBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Media.Media3D; 8 | 9 | namespace winforms_z_buffer 10 | { 11 | public class ZBuffer 12 | { 13 | // minVal is at the top of the stack 14 | double[,] zBufferMap; 15 | Size w; 16 | 17 | public ZBuffer(Size windowSize) 18 | { 19 | w = windowSize; 20 | zBufferMap = new double[windowSize.Width, windowSize.Height]; 21 | for (int i = 0; i < windowSize.Width * windowSize.Height; i++) 22 | zBufferMap[i % windowSize.Width, Ut.F(i / windowSize.Width)] = double.MaxValue; 23 | } 24 | 25 | // get: boolean if z is higher or equal to point (=> true = can draw) 26 | // set: z at point 27 | public double this[double x, double y] 28 | { 29 | get 30 | { 31 | x = Ut.F(x) + (w.Width / 2); 32 | y = Ut.F(y) + (w.Height / 2); 33 | if (x > w.Width - 1 || x < 0 || y < 0 || y > w.Height - 1) return double.MinValue ; 34 | return zBufferMap[Ut.F(x), Ut.F(y)]; 35 | } 36 | set 37 | { 38 | zBufferMap[Ut.F(x) + (w.Width / 2), Ut.F(y) + (w.Height / 2)] = (double)value; 39 | } 40 | } 41 | 42 | public override string ToString() 43 | { 44 | var sb = new StringBuilder(); 45 | 46 | for (int i = 0; i < w.Width; i++) 47 | { 48 | for (int j = 0; j < w.Height; j++) 49 | { 50 | if (zBufferMap[i, j] == double.MinValue) 51 | sb.Append(","); 52 | else 53 | sb.Append(zBufferMap[i, j]); 54 | sb.Append(" "); 55 | } 56 | sb.AppendLine(); 57 | } 58 | 59 | return sb.ToString(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /winforms-z-buffer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace winforms_z_buffer 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new Display()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /winforms-z-buffer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("winforms-z-buffer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("winforms-z-buffer")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("80c2b2e4-5216-4e26-9725-cdca55de81d8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /winforms-z-buffer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace winforms_z_buffer.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("winforms_z_buffer.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /winforms-z-buffer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /winforms-z-buffer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace winforms_z_buffer.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /winforms-z-buffer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace winforms_z_buffer 12 | { 13 | public static class IEnumerableExtensions 14 | { 15 | public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable self) 16 | => self.Select((item, index) => (item, index)); 17 | } 18 | 19 | public static class BitmapExtension 20 | { 21 | public static void SetPixelFast(this Bitmap bmp, int x, int y, Color color) 22 | { 23 | var newValues = new byte[] { color.B, color.G, color.R, 255 }; 24 | 25 | BitmapData data = bmp.LockBits( 26 | new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), 27 | ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb 28 | ); 29 | 30 | if ( 31 | data.Stride * y + 4 * x < data.Stride * data.Height 32 | && data.Stride * y + 4 * x >= 0 33 | && x * 4 < data.Stride 34 | && y < data.Height 35 | && x > 0 36 | ) 37 | unsafe 38 | { 39 | byte* ptr = (byte*)data.Scan0; 40 | 41 | for (int i = 0; i < 4; i++) 42 | ptr[data.Stride * y + 4 * x + i] = newValues[i]; 43 | } 44 | 45 | bmp.UnlockBits(data); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Fill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Windows.Media.Media3D; 6 | using winforms_z_buffer.Utils; 7 | 8 | namespace winforms_z_buffer 9 | { 10 | public static class Fill 11 | // Source https://www.davrous.com/2013/06/21/tutorial-part-4-learning-how-to-write-a-3d-software-engine-in-c-ts-or-js-rasterization-z-buffering/ 12 | { 13 | public static IEnumerable FillTriangle(List vertices) 14 | { 15 | vertices.Sort((x, y) => Ut.F(x.Y - y.Y)); 16 | 17 | var p1 = vertices[0]; 18 | var p2 = vertices[1]; 19 | var p3 = vertices[2]; 20 | 21 | double dP1P2, dP1P3; 22 | 23 | if (p2.Y - p1.Y > 0) 24 | dP1P2 = (p2.X - p1.X) / (p2.Y - p1.Y); 25 | else 26 | dP1P2 = 0; 27 | 28 | if (p3.Y - p1.Y > 0) 29 | dP1P3 = (p3.X - p1.X) / (p3.Y - p1.Y); 30 | else 31 | dP1P3 = 0; 32 | 33 | if (dP1P2 > dP1P3) 34 | { 35 | for (var y = (int)p1.Y; y <= (int)p3.Y; y++) 36 | { 37 | if (y < p2.Y) 38 | foreach (var p in ProcessScanLine(y, p1, p3, p1, p2)) 39 | yield return p; 40 | else 41 | foreach (var p in ProcessScanLine(y, p1, p3, p2, p3)) 42 | yield return p; 43 | } 44 | } 45 | else 46 | { 47 | for (var y = (int)p1.Y; y <= (int)p3.Y; y++) 48 | { 49 | if (y < p2.Y) 50 | foreach (var p in ProcessScanLine(y, p1, p2, p1, p3)) 51 | yield return p; 52 | else 53 | foreach(var p in ProcessScanLine(y, p2, p3, p1, p3)) 54 | yield return p; 55 | } 56 | } 57 | } 58 | 59 | static double Clamp(double value, double min = 0, double max = 1) 60 | { 61 | return Math.Max(min, Math.Min(value, max)); 62 | } 63 | 64 | static double Interpolate(double min, double max, double gradient) 65 | { 66 | return min + (max - min) * Clamp(gradient); 67 | } 68 | 69 | static IEnumerable ProcessScanLine(int y, Point3D pa, Point3D pb, Point3D pc, Point3D pd) 70 | { 71 | var gradient1 = pa.Y != pb.Y ? (y - pa.Y) / (pb.Y - pa.Y) : 1; 72 | var gradient2 = pc.Y != pd.Y ? (y - pc.Y) / (pd.Y - pc.Y) : 1; 73 | 74 | int sx = (int)Interpolate(pa.X, pb.X, gradient1); 75 | int ex = (int)Interpolate(pc.X, pd.X, gradient2); 76 | 77 | double z1 = Interpolate(pa.Z, pb.Z, gradient1); 78 | double z2 = Interpolate(pc.Z, pd.Z, gradient2); 79 | 80 | for (var x = sx; x < ex; x++) 81 | { 82 | float gradient = (x - sx) / (float)(ex - sx); 83 | 84 | var z = Interpolate(z1, z2, gradient); 85 | yield return new Point3D(x, y, z); 86 | } 87 | 88 | for (var x = ex; x < sx; x++) 89 | { 90 | float gradient = (x - sx) / (float)(ex - sx); 91 | 92 | var z = Interpolate(z1, z2, gradient); 93 | yield return new Point3D(x, y, z); 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Line.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Media.Media3D; 8 | 9 | namespace winforms_z_buffer.Utils 10 | { 11 | public static class Line 12 | { 13 | // Bresenham's Line in 3D 14 | // Source https://www.geeksforgeeks.org/bresenhams-algorithm-for-3-d-line-drawing/ 15 | public static List GetPoints(Point3D p1, Point3D p2) 16 | { 17 | double dE = 1; 18 | 19 | List points = new List() { p1 }; 20 | 21 | double dx = Math.Abs(p2.X - p1.X); 22 | double dy = Math.Abs(p2.Y - p1.Y); 23 | double dz = Math.Abs(p2.Z - p1.Z); 24 | 25 | double xs = p2.X > p1.X ? 1 : -1; 26 | double ys = p2.Y > p1.Y ? 1 : -1; 27 | double zs = p2.Z > p1.Z ? 1 : -1; 28 | 29 | double 30 | d1, 31 | d2; 32 | double 33 | x1 = p1.X, 34 | y1 = p1.Y, 35 | z1 = p1.Z; 36 | 37 | if (dx >= dy && dx >= dz) 38 | { 39 | d1 = 2 * dy - dx; 40 | d2 = 2 * dz - dx; 41 | while (Math.Abs(Ut.F(x1) - Ut.F(p2.X)) > dE) 42 | { 43 | x1 += xs; 44 | if (d1 >= 0) 45 | { 46 | y1 += ys; 47 | d1 -= 2 * dx; 48 | } 49 | if (d2 >= 0) 50 | { 51 | z1 += zs; 52 | d2 -= 2 * dx; 53 | } 54 | d1 += 2 * dy; 55 | d2 += 2 * dz; 56 | points.Add(new Point3D(x1, y1, z1)); 57 | } 58 | } 59 | 60 | else if (dy >= dx && dy >= dz) 61 | { 62 | d1 = 2 * dx - dy; 63 | d2 = 2 * dz - dy; 64 | while (Math.Abs(Ut.F(y1) - Ut.F(p2.Y)) > dE) 65 | { 66 | y1 += ys; 67 | if (d1 >= 0) 68 | { 69 | x1 += xs; 70 | d1 -= 2 * dy; 71 | } 72 | if (d2 >= 0) 73 | { 74 | z1 += zs; 75 | d2 -= 2 * dy; 76 | } 77 | d1 += 2 * dx; 78 | d2 += 2 * dz; 79 | points.Add(new Point3D(x1, y1, z1)); 80 | } 81 | } 82 | 83 | else 84 | { 85 | d1 = 2 * dy - dz; 86 | d2 = 2 * dz - dz; 87 | while (Math.Abs(Ut.F(z1) - Ut.F(p2.Z)) > dE) 88 | { 89 | z1 += zs; 90 | if (d1 >= 0) 91 | { 92 | y1 += ys; 93 | d1 -= 2 * dz; 94 | } 95 | if (d2 >= 0) 96 | { 97 | x1 += xs; 98 | d2 -= 2 * dz; 99 | } 100 | d1 += 2 * dy; 101 | d2 += 2 * dx; 102 | points.Add(new Point3D(x1, y1, z1)); 103 | } 104 | } 105 | 106 | points.Add(p2); 107 | 108 | return points; 109 | } 110 | 111 | public static List OutlineTriangle(List vertices) 112 | { 113 | var points = new List(); 114 | 115 | var p1 = vertices[0]; 116 | var p2 = vertices[1]; 117 | var p3 = vertices[2]; 118 | 119 | // Sorting the points in order to always have this order on screen p1, p2 & p3 120 | // with p1 always up (thus having the Y the lowest possible to be near the top screen) 121 | // then p2 between p1 & p3 122 | if (p1.Y > p2.Y) 123 | { 124 | var temp = p2; 125 | p2 = p1; 126 | p1 = temp; 127 | } 128 | 129 | if (p2.Y > p3.Y) 130 | { 131 | var temp = p2; 132 | p2 = p3; 133 | p3 = temp; 134 | } 135 | 136 | if (p1.Y > p2.Y) 137 | { 138 | var temp = p2; 139 | p2 = p1; 140 | p1 = temp; 141 | } 142 | 143 | // inverse slopes 144 | double dP1P2, dP1P3; 145 | 146 | // http://en.wikipedia.org/wiki/Slope 147 | // Computing inverse slopes 148 | if (p2.Y - p1.Y > 0) 149 | dP1P2 = (p2.X - p1.X) / (p2.Y - p1.Y); 150 | else 151 | dP1P2 = 0; 152 | 153 | if (p3.Y - p1.Y > 0) 154 | dP1P3 = (p3.X - p1.X) / (p3.Y - p1.Y); 155 | else 156 | dP1P3 = 0; 157 | 158 | // First case where triangles are like that: 159 | // P1 160 | // - 161 | // -- 162 | // - - 163 | // - - 164 | // - - P2 165 | // - - 166 | // - - 167 | // - 168 | // P3 169 | if (dP1P2 > dP1P3) 170 | { 171 | for (var y = (int)p1.Y; y <= (int)p3.Y; y++) 172 | { 173 | if (y < p2.Y) 174 | { 175 | points.AddRange(ProcessScanLine(y, p1, p3, p1, p2)); 176 | } 177 | else 178 | { 179 | points.AddRange(ProcessScanLine(y, p1, p3, p2, p3)); 180 | } 181 | } 182 | } 183 | // First case where triangles are like that: 184 | // P1 185 | // - 186 | // -- 187 | // - - 188 | // - - 189 | // P2 - - 190 | // - - 191 | // - - 192 | // - 193 | // P3 194 | else 195 | { 196 | for (var y = (int)p1.Y; y <= (int)p3.Y; y++) 197 | { 198 | if (y < p2.Y) 199 | { 200 | points.AddRange(ProcessScanLine(y, p1, p2, p1, p3)); 201 | } 202 | else 203 | { 204 | points.AddRange(ProcessScanLine(y, p2, p3, p1, p3)); 205 | } 206 | } 207 | } 208 | 209 | return points; 210 | } 211 | 212 | static double Clamp(double value, double min = 0, double max = 1) 213 | { 214 | return Math.Max(min, Math.Min(value, max)); 215 | } 216 | 217 | static double Interpolate(double min, double max, double gradient) 218 | { 219 | return min + (max - min) * Clamp(gradient); 220 | } 221 | 222 | static List ProcessScanLine(int y, Point3D pa, Point3D pb, Point3D pc, Point3D pd) 223 | { 224 | var points = new List(); 225 | 226 | var gradient1 = pa.Y != pb.Y ? (y - pa.Y) / (pb.Y - pa.Y) : 1; 227 | var gradient2 = pc.Y != pd.Y ? (y - pc.Y) / (pd.Y - pc.Y) : 1; 228 | 229 | int sx = (int)Interpolate(pa.X, pb.X, gradient1); 230 | int ex = (int)Interpolate(pc.X, pd.X, gradient2); 231 | 232 | double z1 = Interpolate(pa.Z, pb.Z, gradient1); 233 | double z2 = Interpolate(pc.Z, pd.Z, gradient2); 234 | 235 | var x = sx; 236 | 237 | float gradient = (x - sx) / (float)(ex - sx); 238 | 239 | var z = Interpolate(z1, z2, gradient); 240 | points.Add(new Point3D(x, y, z)); 241 | 242 | for (x = sx + 1; x < ex; x++) 243 | { 244 | gradient = (x - sx) / (float)(ex - sx); 245 | 246 | z = Interpolate(z1, z2, gradient); 247 | } 248 | 249 | points.Add(new Point3D(x, y, z)); 250 | 251 | return points; 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Media.Media3D/Matrix3D.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text; 3 | 4 | namespace System.Windows.Media.Media3D 5 | { 6 | 7 | public partial struct Matrix3D 8 | { 9 | // Custom 10 | 11 | public void MultiplyAndNormalizePoint(ref Point4D point) 12 | { 13 | this.MultiplyPoint(ref point); 14 | 15 | if (point.W != 0) 16 | { 17 | point.X /= point.W; 18 | point.Y /= point.W; 19 | point.Z /= point.W; 20 | // point.W /= point.W; 21 | } 22 | } 23 | 24 | public override string ToString() 25 | { 26 | var sb = new StringBuilder("[ "); 27 | 28 | sb.Append(M11); 29 | sb.Append(" "); 30 | sb.Append(M12); 31 | sb.Append(" "); 32 | sb.Append(M13); 33 | sb.Append(" "); 34 | sb.Append(M14); 35 | sb.Append("\n "); 36 | 37 | sb.Append(M21); 38 | sb.Append(" "); 39 | sb.Append(M22); 40 | sb.Append(" "); 41 | sb.Append(M23); 42 | sb.Append(" "); 43 | sb.Append(M24); 44 | sb.Append("\n "); 45 | 46 | sb.Append(M31); 47 | sb.Append(" "); 48 | sb.Append(M32); 49 | sb.Append(" "); 50 | sb.Append(M33); 51 | sb.Append(" "); 52 | sb.Append(M34); 53 | sb.Append("\n "); 54 | 55 | sb.Append(OffsetX); 56 | sb.Append(" "); 57 | sb.Append(OffsetY); 58 | sb.Append(" "); 59 | sb.Append(OffsetZ); 60 | sb.Append(" "); 61 | sb.Append(M44); 62 | sb.Append(" ]"); 63 | 64 | return sb.ToString(); 65 | } 66 | 67 | //------------------------------------------------------ 68 | // 69 | // Constructors 70 | // 71 | //------------------------------------------------------ 72 | 73 | #region Constructors 74 | 75 | /// 76 | /// Constructor that sets matrix's initial values. 77 | /// 78 | /// Value of the (1,1) field of the new matrix. 79 | /// Value of the (1,2) field of the new matrix. 80 | /// Value of the (1,3) field of the new matrix. 81 | /// Value of the (1,4) field of the new matrix. 82 | /// Value of the (2,1) field of the new matrix. 83 | /// Value of the (2,2) field of the new matrix. 84 | /// Value of the (2,3) field of the new matrix. 85 | /// Value of the (2,4) field of the new matrix. 86 | /// Value of the (3,1) field of the new matrix. 87 | /// Value of the (3,2) field of the new matrix. 88 | /// Value of the (3,3) field of the new matrix. 89 | /// Value of the (3,4) field of the new matrix. 90 | /// Value of the X offset field of the new matrix. 91 | /// Value of the Y offset field of the new matrix. 92 | /// Value of the Z offset field of the new matrix. 93 | /// Value of the (4,4) field of the new matrix. 94 | public Matrix3D(double m11, double m12, double m13, double m14, 95 | double m21, double m22, double m23, double m24, 96 | double m31, double m32, double m33, double m34, 97 | double offsetX, double offsetY, double offsetZ, double m44) 98 | { 99 | _m11 = m11; 100 | _m12 = m12; 101 | _m13 = m13; 102 | _m14 = m14; 103 | _m21 = m21; 104 | _m22 = m22; 105 | _m23 = m23; 106 | _m24 = m24; 107 | _m31 = m31; 108 | _m32 = m32; 109 | _m33 = m33; 110 | _m34 = m34; 111 | _offsetX = offsetX; 112 | _offsetY = offsetY; 113 | _offsetZ = offsetZ; 114 | _m44 = m44; 115 | 116 | // This is not known to be an identity matrix so we need 117 | // to change our flag from it's default value. We use the field 118 | // in the ctor rather than the property because of CS0188. 119 | _isNotKnownToBeIdentity = true; 120 | } 121 | 122 | #endregion Constructors 123 | 124 | //------------------------------------------------------ 125 | // 126 | // Identity 127 | // 128 | //------------------------------------------------------ 129 | 130 | #region Identity 131 | // Identity 132 | /// 133 | /// Returns identity matrix. 134 | /// 135 | public static Matrix3D Identity 136 | { 137 | get 138 | { 139 | return s_identity; 140 | } 141 | } 142 | 143 | /// 144 | /// Sets matrix to identity. 145 | /// 146 | public void SetIdentity() 147 | { 148 | this = s_identity; 149 | } 150 | 151 | /// 152 | /// Returns whether the matrix is identity. 153 | /// 154 | public bool IsIdentity 155 | { 156 | get 157 | { 158 | if (IsDistinguishedIdentity) 159 | { 160 | return true; 161 | } 162 | else 163 | { 164 | // Otherwise check all elements one by one. 165 | if (_m11 == 1.0 && _m12 == 0.0 && _m13 == 0.0 && _m14 == 0.0 && 166 | _m21 == 0.0 && _m22 == 1.0 && _m23 == 0.0 && _m24 == 0.0 && 167 | _m31 == 0.0 && _m32 == 0.0 && _m33 == 1.0 && _m34 == 0.0 && 168 | _offsetX == 0.0 && _offsetY == 0.0 && _offsetZ == 0.0 && _m44 == 1.0) 169 | { 170 | // If matrix is identity, cache this with the IsDistinguishedIdentity flag. 171 | IsDistinguishedIdentity = true; 172 | return true; 173 | } 174 | else 175 | { 176 | return false; 177 | } 178 | } 179 | } 180 | } 181 | 182 | #endregion Identity 183 | 184 | //------------------------------------------------------ 185 | // 186 | // Math Operations 187 | // 188 | //------------------------------------------------------ 189 | 190 | #region Math Operations 191 | // Math operations 192 | 193 | /// 194 | /// Prepends the given matrix to the current matrix. 195 | /// 196 | /// Matrix to prepend. 197 | public void Prepend(Matrix3D matrix) 198 | { 199 | this = matrix * this; 200 | } 201 | 202 | /// 203 | /// Appends the given matrix to the current matrix. 204 | /// 205 | /// Matrix to append. 206 | public void Append(Matrix3D matrix) 207 | { 208 | this *= matrix; 209 | } 210 | 211 | #endregion Math Operations 212 | 213 | //------------------------------------------------------ 214 | // 215 | // Rotate 216 | // 217 | //------------------------------------------------------ 218 | 219 | #region Rotate 220 | 221 | /// 222 | /// Appends rotation transform to the current matrix. 223 | /// 224 | /// Quaternion representing rotation. 225 | public void Rotate(Quaternion quaternion) 226 | { 227 | Point3D center = new Point3D(); 228 | 229 | this *= CreateRotationMatrix(ref quaternion, ref center); 230 | } 231 | 232 | /// 233 | /// Prepends rotation transform to the current matrix. 234 | /// 235 | /// Quaternion representing rotation. 236 | public void RotatePrepend(Quaternion quaternion) 237 | { 238 | Point3D center = new Point3D(); 239 | 240 | this = CreateRotationMatrix(ref quaternion, ref center) * this; 241 | } 242 | 243 | /// 244 | /// Appends rotation transform to the current matrix. 245 | /// 246 | /// Quaternion representing rotation. 247 | /// Center to rotate around. 248 | public void RotateAt(Quaternion quaternion, Point3D center) 249 | { 250 | this *= CreateRotationMatrix(ref quaternion, ref center); 251 | } 252 | 253 | /// 254 | /// Prepends rotation transform to the current matrix. 255 | /// 256 | /// Quaternion representing rotation. 257 | /// Center to rotate around. 258 | public void RotateAtPrepend(Quaternion quaternion, Point3D center) 259 | { 260 | this = CreateRotationMatrix(ref quaternion, ref center) * this; 261 | } 262 | 263 | #endregion Rotate 264 | 265 | 266 | //------------------------------------------------------ 267 | // 268 | // Scale 269 | // 270 | //------------------------------------------------------ 271 | 272 | #region Scale 273 | 274 | /// 275 | /// Appends scale transform to the current matrix. 276 | /// 277 | /// Scaling vector for transformation. 278 | public void Scale(Vector3D scale) 279 | { 280 | if (IsDistinguishedIdentity) 281 | { 282 | SetScaleMatrix(ref scale); 283 | } 284 | else 285 | { 286 | _m11 *= scale.X; _m12 *= scale.Y; _m13 *= scale.Z; 287 | _m21 *= scale.X; _m22 *= scale.Y; _m23 *= scale.Z; 288 | _m31 *= scale.X; _m32 *= scale.Y; _m33 *= scale.Z; 289 | _offsetX *= scale.X; _offsetY *= scale.Y; _offsetZ *= scale.Z; 290 | } 291 | } 292 | 293 | /// 294 | /// Prepends scale transform to the current matrix. 295 | /// 296 | /// Scaling vector for transformation. 297 | public void ScalePrepend(Vector3D scale) 298 | { 299 | if (IsDistinguishedIdentity) 300 | { 301 | SetScaleMatrix(ref scale); 302 | } 303 | else 304 | { 305 | _m11 *= scale.X; _m12 *= scale.X; _m13 *= scale.X; _m14 *= scale.X; 306 | _m21 *= scale.Y; _m22 *= scale.Y; _m23 *= scale.Y; _m24 *= scale.Y; 307 | _m31 *= scale.Z; _m32 *= scale.Z; _m33 *= scale.Z; _m34 *= scale.Z; 308 | } 309 | } 310 | 311 | /// 312 | /// Appends scale transform to the current matrix. 313 | /// 314 | /// Scaling vector for transformation. 315 | /// Point around which to scale. 316 | public void ScaleAt(Vector3D scale, Point3D center) 317 | { 318 | if (IsDistinguishedIdentity) 319 | { 320 | SetScaleMatrix(ref scale, ref center); 321 | } 322 | else 323 | { 324 | double tmp = _m14 * center.X; 325 | _m11 = tmp + scale.X * (_m11 - tmp); 326 | tmp = _m14 * center.Y; 327 | _m12 = tmp + scale.Y * (_m12 - tmp); 328 | tmp = _m14 * center.Z; 329 | _m13 = tmp + scale.Z * (_m13 - tmp); 330 | 331 | tmp = _m24 * center.X; 332 | _m21 = tmp + scale.X * (_m21 - tmp); 333 | tmp = _m24 * center.Y; 334 | _m22 = tmp + scale.Y * (_m22 - tmp); 335 | tmp = _m24 * center.Z; 336 | _m23 = tmp + scale.Z * (_m23 - tmp); 337 | 338 | tmp = _m34 * center.X; 339 | _m31 = tmp + scale.X * (_m31 - tmp); 340 | tmp = _m34 * center.Y; 341 | _m32 = tmp + scale.Y * (_m32 - tmp); 342 | tmp = _m34 * center.Z; 343 | _m33 = tmp + scale.Z * (_m33 - tmp); 344 | 345 | tmp = _m44 * center.X; 346 | _offsetX = tmp + scale.X * (_offsetX - tmp); 347 | tmp = _m44 * center.Y; 348 | _offsetY = tmp + scale.Y * (_offsetY - tmp); 349 | tmp = _m44 * center.Z; 350 | _offsetZ = tmp + scale.Z * (_offsetZ - tmp); 351 | } 352 | } 353 | 354 | /// 355 | /// Prepends scale transform to the current matrix. 356 | /// 357 | /// Scaling vector for transformation. 358 | /// Point around which to scale. 359 | public void ScaleAtPrepend(Vector3D scale, Point3D center) 360 | { 361 | if (IsDistinguishedIdentity) 362 | { 363 | SetScaleMatrix(ref scale, ref center); 364 | } 365 | else 366 | { 367 | double csx = center.X - center.X * scale.X; 368 | double csy = center.Y - center.Y * scale.Y; 369 | double csz = center.Z - center.Z * scale.Z; 370 | 371 | // We have to set the bottom row first because it depends 372 | // on values that will change 373 | _offsetX += _m11 * csx + _m21 * csy + _m31 * csz; 374 | _offsetY += _m12 * csx + _m22 * csy + _m32 * csz; 375 | _offsetZ += _m13 * csx + _m23 * csy + _m33 * csz; 376 | _m44 += _m14 * csx + _m24 * csy + _m34 * csz; 377 | 378 | _m11 *= scale.X; _m12 *= scale.X; _m13 *= scale.X; _m14 *= scale.X; 379 | _m21 *= scale.Y; _m22 *= scale.Y; _m23 *= scale.Y; _m24 *= scale.Y; 380 | _m31 *= scale.Z; _m32 *= scale.Z; _m33 *= scale.Z; _m34 *= scale.Z; 381 | } 382 | } 383 | 384 | #endregion Scale 385 | 386 | //------------------------------------------------------ 387 | // 388 | // Translate 389 | // 390 | //------------------------------------------------------ 391 | 392 | #region Translate 393 | /// 394 | /// Appends translation transform to the current matrix. 395 | /// 396 | /// Offset vector for transformation. 397 | public void Translate(Vector3D offset) 398 | { 399 | if (IsDistinguishedIdentity) 400 | { 401 | SetTranslationMatrix(ref offset); 402 | } 403 | else 404 | { 405 | _m11 += _m14 * offset.X; _m12 += _m14 * offset.Y; _m13 += _m14 * offset.Z; 406 | _m21 += _m24 * offset.X; _m22 += _m24 * offset.Y; _m23 += _m24 * offset.Z; 407 | _m31 += _m34 * offset.X; _m32 += _m34 * offset.Y; _m33 += _m34 * offset.Z; 408 | _offsetX += _m44 * offset.X; _offsetY += _m44 * offset.Y; _offsetZ += _m44 * offset.Z; 409 | } 410 | } 411 | 412 | /// 413 | /// Prepends translation transform to the current matrix. 414 | /// 415 | /// Offset vector for transformation. 416 | public void TranslatePrepend(Vector3D offset) 417 | { 418 | if (IsDistinguishedIdentity) 419 | { 420 | SetTranslationMatrix(ref offset); 421 | } 422 | else 423 | { 424 | _offsetX += _m11 * offset.X + _m21 * offset.Y + _m31 * offset.Z; 425 | _offsetY += _m12 * offset.X + _m22 * offset.Y + _m32 * offset.Z; 426 | _offsetZ += _m13 * offset.X + _m23 * offset.Y + _m33 * offset.Z; 427 | _m44 += _m14 * offset.X + _m24 * offset.Y + _m34 * offset.Z; 428 | } 429 | } 430 | 431 | #endregion Translate 432 | 433 | //------------------------------------------------------ 434 | // 435 | // Multiplication 436 | // 437 | //------------------------------------------------------ 438 | 439 | #region Multiplication 440 | 441 | /// 442 | /// Matrix multiplication. 443 | /// 444 | /// Matrix to multiply. 445 | /// Matrix by which the first matrix is multiplied. 446 | /// Result of multiplication. 447 | public static Matrix3D operator *(Matrix3D matrix1, Matrix3D matrix2) 448 | { 449 | // Check if multiplying by identity. 450 | if (matrix1.IsDistinguishedIdentity) 451 | return matrix2; 452 | if (matrix2.IsDistinguishedIdentity) 453 | return matrix1; 454 | 455 | // Regular 4x4 matrix multiplication. 456 | Matrix3D result = new Matrix3D( 457 | matrix1._m11 * matrix2._m11 + matrix1._m12 * matrix2._m21 + 458 | matrix1._m13 * matrix2._m31 + matrix1._m14 * matrix2._offsetX, 459 | matrix1._m11 * matrix2._m12 + matrix1._m12 * matrix2._m22 + 460 | matrix1._m13 * matrix2._m32 + matrix1._m14 * matrix2._offsetY, 461 | matrix1._m11 * matrix2._m13 + matrix1._m12 * matrix2._m23 + 462 | matrix1._m13 * matrix2._m33 + matrix1._m14 * matrix2._offsetZ, 463 | matrix1._m11 * matrix2._m14 + matrix1._m12 * matrix2._m24 + 464 | matrix1._m13 * matrix2._m34 + matrix1._m14 * matrix2._m44, 465 | matrix1._m21 * matrix2._m11 + matrix1._m22 * matrix2._m21 + 466 | matrix1._m23 * matrix2._m31 + matrix1._m24 * matrix2._offsetX, 467 | matrix1._m21 * matrix2._m12 + matrix1._m22 * matrix2._m22 + 468 | matrix1._m23 * matrix2._m32 + matrix1._m24 * matrix2._offsetY, 469 | matrix1._m21 * matrix2._m13 + matrix1._m22 * matrix2._m23 + 470 | matrix1._m23 * matrix2._m33 + matrix1._m24 * matrix2._offsetZ, 471 | matrix1._m21 * matrix2._m14 + matrix1._m22 * matrix2._m24 + 472 | matrix1._m23 * matrix2._m34 + matrix1._m24 * matrix2._m44, 473 | matrix1._m31 * matrix2._m11 + matrix1._m32 * matrix2._m21 + 474 | matrix1._m33 * matrix2._m31 + matrix1._m34 * matrix2._offsetX, 475 | matrix1._m31 * matrix2._m12 + matrix1._m32 * matrix2._m22 + 476 | matrix1._m33 * matrix2._m32 + matrix1._m34 * matrix2._offsetY, 477 | matrix1._m31 * matrix2._m13 + matrix1._m32 * matrix2._m23 + 478 | matrix1._m33 * matrix2._m33 + matrix1._m34 * matrix2._offsetZ, 479 | matrix1._m31 * matrix2._m14 + matrix1._m32 * matrix2._m24 + 480 | matrix1._m33 * matrix2._m34 + matrix1._m34 * matrix2._m44, 481 | matrix1._offsetX * matrix2._m11 + matrix1._offsetY * matrix2._m21 + 482 | matrix1._offsetZ * matrix2._m31 + matrix1._m44 * matrix2._offsetX, 483 | matrix1._offsetX * matrix2._m12 + matrix1._offsetY * matrix2._m22 + 484 | matrix1._offsetZ * matrix2._m32 + matrix1._m44 * matrix2._offsetY, 485 | matrix1._offsetX * matrix2._m13 + matrix1._offsetY * matrix2._m23 + 486 | matrix1._offsetZ * matrix2._m33 + matrix1._m44 * matrix2._offsetZ, 487 | matrix1._offsetX * matrix2._m14 + matrix1._offsetY * matrix2._m24 + 488 | matrix1._offsetZ * matrix2._m34 + matrix1._m44 * matrix2._m44); 489 | 490 | return result; 491 | } 492 | 493 | /// 494 | /// Matrix multiplication. 495 | /// 496 | /// Matrix to multiply. 497 | /// Matrix by which the first matrix is multiplied. 498 | /// Result of multiplication. 499 | public static Matrix3D Multiply(Matrix3D matrix1, Matrix3D matrix2) 500 | { 501 | return (matrix1 * matrix2); 502 | } 503 | 504 | #endregion Multiplication 505 | 506 | //------------------------------------------------------ 507 | // 508 | // Transformation Services 509 | // 510 | //------------------------------------------------------ 511 | 512 | #region Transformation Services 513 | 514 | /// 515 | /// Transforms the given Point3D by this matrix, projecting the 516 | /// result back into the W=1 plane. 517 | /// 518 | /// Point to transform. 519 | /// Transformed point. 520 | public Point3D Transform(Point3D point) 521 | { 522 | MultiplyPoint(ref point); 523 | return point; 524 | } 525 | 526 | /// 527 | /// Transforms the given Point3Ds by this matrix, projecting the 528 | /// results back into the W=1 plane. 529 | /// 530 | /// Points to transform. 531 | public void Transform(Point3D[] points) 532 | { 533 | if (points != null) 534 | { 535 | for (int i = 0; i < points.Length; i++) 536 | { 537 | MultiplyPoint(ref points[i]); 538 | } 539 | } 540 | } 541 | 542 | /// 543 | /// Transforms the given point by the current matrix. 544 | /// 545 | /// Point to transform. 546 | /// Transformed point. 547 | public Point4D Transform(Point4D point) 548 | { 549 | MultiplyPoint(ref point); 550 | return point; 551 | } 552 | 553 | /// 554 | /// Transforms the given points by the current matrix. 555 | /// 556 | /// Points to transform. 557 | public void Transform(Point4D[] points) 558 | { 559 | if (points != null) 560 | { 561 | for (int i = 0; i < points.Length; i++) 562 | { 563 | MultiplyPoint(ref points[i]); 564 | } 565 | } 566 | } 567 | 568 | /// 569 | /// Transforms the given vector by the current matrix. 570 | /// 571 | /// Vector to transform. 572 | /// Transformed vector. 573 | public Vector3D Transform(Vector3D vector) 574 | { 575 | MultiplyVector(ref vector); 576 | return vector; 577 | } 578 | 579 | /// 580 | /// Transforms the given vectors by the current matrix. 581 | /// 582 | /// Vectors to transform. 583 | public void Transform(Vector3D[] vectors) 584 | { 585 | if (vectors != null) 586 | { 587 | for (int i = 0; i < vectors.Length; i++) 588 | { 589 | MultiplyVector(ref vectors[i]); 590 | } 591 | } 592 | } 593 | 594 | #endregion Transformation Services 595 | 596 | 597 | /// 598 | /// Determines whether the matrix is affine. 599 | /// 600 | public bool IsAffine 601 | { 602 | get 603 | { 604 | return (IsDistinguishedIdentity || 605 | (_m14 == 0.0 && _m24 == 0.0 && _m34 == 0.0 && _m44 == 1.0)); 606 | } 607 | } 608 | 609 | 610 | //------------------------------------------------------ 611 | // 612 | // Inversion 613 | // 614 | //------------------------------------------------------ 615 | 616 | #region Inversion 617 | 618 | /// 619 | /// Matrix determinant. 620 | /// 621 | public double Determinant 622 | { 623 | get 624 | { 625 | if (IsDistinguishedIdentity) 626 | return 1.0; 627 | if (IsAffine) 628 | return GetNormalizedAffineDeterminant(); 629 | 630 | // NOTE: The beginning of this code is duplicated between 631 | // the Invert method and the Determinant property. 632 | 633 | // compute all six 2x2 determinants of 2nd two columns 634 | double y01 = _m13 * _m24 - _m23 * _m14; 635 | double y02 = _m13 * _m34 - _m33 * _m14; 636 | double y03 = _m13 * _m44 - _offsetZ * _m14; 637 | double y12 = _m23 * _m34 - _m33 * _m24; 638 | double y13 = _m23 * _m44 - _offsetZ * _m24; 639 | double y23 = _m33 * _m44 - _offsetZ * _m34; 640 | 641 | // Compute 3x3 cofactors for 1st the column 642 | double z30 = _m22 * y02 - _m32 * y01 - _m12 * y12; 643 | double z20 = _m12 * y13 - _m22 * y03 + _offsetY * y01; 644 | double z10 = _m32 * y03 - _offsetY * y02 - _m12 * y23; 645 | double z00 = _m22 * y23 - _m32 * y13 + _offsetY * y12; 646 | 647 | return _offsetX * z30 + _m31 * z20 + _m21 * z10 + _m11 * z00; 648 | } 649 | } 650 | 651 | /// 652 | /// Whether the matrix has an inverse. 653 | /// 654 | 655 | 656 | /// 657 | /// Computes, and substitutes in-place, the inverse of a matrix. 658 | /// The determinant of the matrix must be nonzero, otherwise the matrix is not invertible. 659 | /// In this case it will throw InvalidOperationException exception. 660 | /// 661 | /// 662 | /// This will throw InvalidOperationException if the matrix is not invertible. 663 | /// 664 | 665 | #endregion Inversion 666 | 667 | //------------------------------------------------------ 668 | // 669 | // Individual Members 670 | // 671 | //------------------------------------------------------ 672 | 673 | #region Individual Members 674 | 675 | /// 676 | /// Retrieves or sets (1,1) value of the matrix. 677 | /// 678 | public double M11 679 | { 680 | get 681 | { 682 | if (IsDistinguishedIdentity) 683 | { 684 | return 1.0; 685 | } 686 | else 687 | { 688 | return _m11; 689 | } 690 | } 691 | set 692 | { 693 | if (IsDistinguishedIdentity) 694 | { 695 | this = s_identity; 696 | IsDistinguishedIdentity = false; 697 | } 698 | _m11 = value; 699 | } 700 | } 701 | 702 | /// 703 | /// Retrieves or sets (1,2) value of the matrix. 704 | /// 705 | public double M12 706 | { 707 | get 708 | { 709 | return _m12; 710 | } 711 | set 712 | { 713 | if (IsDistinguishedIdentity) 714 | { 715 | this = s_identity; 716 | IsDistinguishedIdentity = false; 717 | } 718 | _m12 = value; 719 | } 720 | } 721 | 722 | /// 723 | /// Retrieves or sets (1,3) value of the matrix. 724 | /// 725 | public double M13 726 | { 727 | get 728 | { 729 | return _m13; 730 | } 731 | set 732 | { 733 | if (IsDistinguishedIdentity) 734 | { 735 | this = s_identity; 736 | IsDistinguishedIdentity = false; 737 | } 738 | _m13 = value; 739 | } 740 | } 741 | 742 | /// 743 | /// Retrieves or sets (1,4) value of the matrix. 744 | /// 745 | public double M14 746 | { 747 | get 748 | { 749 | return _m14; 750 | } 751 | set 752 | { 753 | if (IsDistinguishedIdentity) 754 | { 755 | this = s_identity; 756 | IsDistinguishedIdentity = false; 757 | } 758 | _m14 = value; 759 | } 760 | } 761 | 762 | 763 | /// 764 | /// Retrieves or sets (2,1) value of the matrix. 765 | /// 766 | public double M21 767 | { 768 | get 769 | { 770 | return _m21; 771 | } 772 | set 773 | { 774 | if (IsDistinguishedIdentity) 775 | { 776 | this = s_identity; 777 | IsDistinguishedIdentity = false; 778 | } 779 | _m21 = value; 780 | } 781 | } 782 | 783 | /// 784 | /// Retrieves or sets (2,2) value of the matrix. 785 | /// 786 | public double M22 787 | { 788 | get 789 | { 790 | if (IsDistinguishedIdentity) 791 | { 792 | return 1.0; 793 | } 794 | else 795 | { 796 | return _m22; 797 | } 798 | } 799 | set 800 | { 801 | if (IsDistinguishedIdentity) 802 | { 803 | this = s_identity; 804 | IsDistinguishedIdentity = false; 805 | } 806 | _m22 = value; 807 | } 808 | } 809 | 810 | /// 811 | /// Retrieves or sets (2,3) value of the matrix. 812 | /// 813 | public double M23 814 | { 815 | get 816 | { 817 | return _m23; 818 | } 819 | set 820 | { 821 | if (IsDistinguishedIdentity) 822 | { 823 | this = s_identity; 824 | IsDistinguishedIdentity = false; 825 | } 826 | _m23 = value; 827 | } 828 | } 829 | 830 | /// 831 | /// Retrieves or sets (2,4) value of the matrix. 832 | /// 833 | public double M24 834 | { 835 | get 836 | { 837 | return _m24; 838 | } 839 | set 840 | { 841 | if (IsDistinguishedIdentity) 842 | { 843 | this = s_identity; 844 | IsDistinguishedIdentity = false; 845 | } 846 | _m24 = value; 847 | } 848 | } 849 | 850 | 851 | 852 | /// 853 | /// Retrieves or sets (3,1) value of the matrix. 854 | /// 855 | public double M31 856 | { 857 | get 858 | { 859 | return _m31; 860 | } 861 | set 862 | { 863 | if (IsDistinguishedIdentity) 864 | { 865 | this = s_identity; 866 | IsDistinguishedIdentity = false; 867 | } 868 | _m31 = value; 869 | } 870 | } 871 | 872 | /// 873 | /// Retrieves or sets (3,2) value of the matrix. 874 | /// 875 | public double M32 876 | { 877 | get 878 | { 879 | return _m32; 880 | } 881 | set 882 | { 883 | if (IsDistinguishedIdentity) 884 | { 885 | this = s_identity; 886 | IsDistinguishedIdentity = false; 887 | } 888 | _m32 = value; 889 | } 890 | } 891 | 892 | /// 893 | /// Retrieves or sets (3,3) value of the matrix. 894 | /// 895 | public double M33 896 | { 897 | get 898 | { 899 | if (IsDistinguishedIdentity) 900 | { 901 | return 1.0; 902 | } 903 | else 904 | { 905 | return _m33; 906 | } 907 | } 908 | set 909 | { 910 | if (IsDistinguishedIdentity) 911 | { 912 | this = s_identity; 913 | IsDistinguishedIdentity = false; 914 | } 915 | _m33 = value; 916 | } 917 | } 918 | 919 | /// 920 | /// Retrieves or sets (3,4) value of the matrix. 921 | /// 922 | public double M34 923 | { 924 | get 925 | { 926 | return _m34; 927 | } 928 | set 929 | { 930 | if (IsDistinguishedIdentity) 931 | { 932 | this = s_identity; 933 | IsDistinguishedIdentity = false; 934 | } 935 | _m34 = value; 936 | } 937 | } 938 | 939 | 940 | /// 941 | /// Retrieves or sets X offset of the matrix. 942 | /// 943 | public double OffsetX 944 | { 945 | get 946 | { 947 | return _offsetX; 948 | } 949 | set 950 | { 951 | if (IsDistinguishedIdentity) 952 | { 953 | this = s_identity; 954 | IsDistinguishedIdentity = false; 955 | } 956 | _offsetX = value; 957 | } 958 | } 959 | /// 960 | /// Retrieves or sets Y offset of the matrix. 961 | /// 962 | public double OffsetY 963 | { 964 | get 965 | { 966 | return _offsetY; 967 | } 968 | set 969 | { 970 | if (IsDistinguishedIdentity) 971 | { 972 | this = s_identity; 973 | IsDistinguishedIdentity = false; 974 | } 975 | _offsetY = value; 976 | } 977 | } 978 | /// 979 | /// Retrieves or sets Z offset of the matrix. 980 | /// 981 | public double OffsetZ 982 | { 983 | get 984 | { 985 | return _offsetZ; 986 | } 987 | set 988 | { 989 | if (IsDistinguishedIdentity) 990 | { 991 | this = s_identity; 992 | IsDistinguishedIdentity = false; 993 | } 994 | _offsetZ = value; 995 | } 996 | } 997 | 998 | 999 | /// 1000 | /// Retrieves or sets (4,4) value of the matrix. 1001 | /// 1002 | public double M44 1003 | { 1004 | get 1005 | { 1006 | if (IsDistinguishedIdentity) 1007 | { 1008 | return 1.0; 1009 | } 1010 | else 1011 | { 1012 | return _m44; 1013 | } 1014 | } 1015 | set 1016 | { 1017 | if (IsDistinguishedIdentity) 1018 | { 1019 | this = s_identity; 1020 | IsDistinguishedIdentity = false; 1021 | } 1022 | _m44 = value; 1023 | } 1024 | } 1025 | 1026 | 1027 | 1028 | #endregion Individual Members 1029 | 1030 | //------------------------------------------------------ 1031 | // 1032 | // Internal Methods 1033 | // 1034 | //------------------------------------------------------ 1035 | 1036 | #region Internal Methods 1037 | 1038 | internal void SetScaleMatrix(ref Vector3D scale) 1039 | { 1040 | Debug.Assert(IsDistinguishedIdentity); 1041 | 1042 | _m11 = scale.X; 1043 | _m22 = scale.Y; 1044 | _m33 = scale.Z; 1045 | _m44 = 1.0; 1046 | 1047 | IsDistinguishedIdentity = false; 1048 | } 1049 | 1050 | internal void SetScaleMatrix(ref Vector3D scale, ref Point3D center) 1051 | { 1052 | Debug.Assert(IsDistinguishedIdentity); 1053 | 1054 | _m11 = scale.X; 1055 | _m22 = scale.Y; 1056 | _m33 = scale.Z; 1057 | _m44 = 1.0; 1058 | 1059 | _offsetX = center.X - center.X * scale.X; 1060 | _offsetY = center.Y - center.Y * scale.Y; 1061 | _offsetZ = center.Z - center.Z * scale.Z; 1062 | 1063 | IsDistinguishedIdentity = false; 1064 | } 1065 | 1066 | internal void SetTranslationMatrix(ref Vector3D offset) 1067 | { 1068 | Debug.Assert(IsDistinguishedIdentity); 1069 | 1070 | _m11 = _m22 = _m33 = _m44 = 1.0; 1071 | 1072 | _offsetX = offset.X; 1073 | _offsetY = offset.Y; 1074 | _offsetZ = offset.Z; 1075 | 1076 | IsDistinguishedIdentity = false; 1077 | } 1078 | 1079 | // Creates a rotation matrix given a quaternion and center. 1080 | // 1081 | // Quaternion and center are passed by reference for performance 1082 | // only and are not modified. 1083 | // 1084 | internal static Matrix3D CreateRotationMatrix(ref Quaternion quaternion, ref Point3D center) 1085 | { 1086 | Matrix3D matrix = s_identity; 1087 | matrix.IsDistinguishedIdentity = false; // Will be using direct member access 1088 | double wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; 1089 | 1090 | x2 = quaternion.X + quaternion.X; 1091 | y2 = quaternion.Y + quaternion.Y; 1092 | z2 = quaternion.Z + quaternion.Z; 1093 | xx = quaternion.X * x2; 1094 | xy = quaternion.X * y2; 1095 | xz = quaternion.X * z2; 1096 | yy = quaternion.Y * y2; 1097 | yz = quaternion.Y * z2; 1098 | zz = quaternion.Z * z2; 1099 | wx = quaternion.W * x2; 1100 | wy = quaternion.W * y2; 1101 | wz = quaternion.W * z2; 1102 | 1103 | matrix._m11 = 1.0 - (yy + zz); 1104 | matrix._m12 = xy + wz; 1105 | matrix._m13 = xz - wy; 1106 | matrix._m21 = xy - wz; 1107 | matrix._m22 = 1.0 - (xx + zz); 1108 | matrix._m23 = yz + wx; 1109 | matrix._m31 = xz + wy; 1110 | matrix._m32 = yz - wx; 1111 | matrix._m33 = 1.0 - (xx + yy); 1112 | 1113 | if (center.X != 0 || center.Y != 0 || center.Z != 0) 1114 | { 1115 | matrix._offsetX = -center.X * matrix._m11 - center.Y * matrix._m21 - center.Z * matrix._m31 + center.X; 1116 | matrix._offsetY = -center.X * matrix._m12 - center.Y * matrix._m22 - center.Z * matrix._m32 + center.Y; 1117 | matrix._offsetZ = -center.X * matrix._m13 - center.Y * matrix._m23 - center.Z * matrix._m33 + center.Z; 1118 | } 1119 | 1120 | return matrix; 1121 | } 1122 | 1123 | // Multiplies the given Point3D by this matrix, projecting the 1124 | // result back into the W=1 plane. 1125 | // 1126 | // The point is modified in place for performance. 1127 | // 1128 | internal void MultiplyPoint(ref Point3D point) 1129 | { 1130 | if (IsDistinguishedIdentity) 1131 | return; 1132 | 1133 | double x = point.X; 1134 | double y = point.Y; 1135 | double z = point.Z; 1136 | 1137 | point.X = x * _m11 + y * _m21 + z * _m31 + _offsetX; 1138 | point.Y = x * _m12 + y * _m22 + z * _m32 + _offsetY; 1139 | point.Z = x * _m13 + y * _m23 + z * _m33 + _offsetZ; 1140 | 1141 | if (!IsAffine) 1142 | { 1143 | double w = x * _m14 + y * _m24 + z * _m34 + _m44; 1144 | 1145 | point.X /= w; 1146 | point.Y /= w; 1147 | point.Z /= w; 1148 | } 1149 | } 1150 | 1151 | // Multiplies the given Point4D by this matrix. 1152 | // 1153 | // The point is modified in place for performance. 1154 | // 1155 | internal void MultiplyPoint(ref Point4D point) 1156 | { 1157 | if (IsDistinguishedIdentity) 1158 | return; 1159 | 1160 | double x = point.X; 1161 | double y = point.Y; 1162 | double z = point.Z; 1163 | double w = point.W; 1164 | 1165 | point.X = x * _m11 + y * _m21 + z * _m31 + w * _offsetX; 1166 | point.Y = x * _m12 + y * _m22 + z * _m32 + w * _offsetY; 1167 | point.Z = x * _m13 + y * _m23 + z * _m33 + w * _offsetZ; 1168 | point.W = x * _m14 + y * _m24 + z * _m34 + w * _m44; 1169 | } 1170 | 1171 | // Multiplies the given Vector3D by this matrix. 1172 | // 1173 | // The vector is modified in place for performance. 1174 | // 1175 | internal void MultiplyVector(ref Vector3D vector) 1176 | { 1177 | if (IsDistinguishedIdentity) 1178 | return; 1179 | 1180 | double x = vector.X; 1181 | double y = vector.Y; 1182 | double z = vector.Z; 1183 | 1184 | // Do not apply _offset to vectors. 1185 | vector.X = x * _m11 + y * _m21 + z * _m31; 1186 | vector.Y = x * _m12 + y * _m22 + z * _m32; 1187 | vector.Z = x * _m13 + y * _m23 + z * _m33; 1188 | } 1189 | 1190 | // Computes the determinant of the matrix assuming that it's 1191 | // fourth column is 0,0,0,1 and it isn't identity 1192 | internal double GetNormalizedAffineDeterminant() 1193 | { 1194 | Debug.Assert(!IsDistinguishedIdentity); 1195 | Debug.Assert(IsAffine); 1196 | 1197 | // NOTE: The beginning of this code is duplicated between 1198 | // GetNormalizedAffineDeterminant() and NormalizedAffineInvert() 1199 | 1200 | double z20 = _m12 * _m23 - _m22 * _m13; 1201 | double z10 = _m32 * _m13 - _m12 * _m33; 1202 | double z00 = _m22 * _m33 - _m32 * _m23; 1203 | 1204 | return _m31 * z20 + _m21 * z10 + _m11 * z00; 1205 | } 1206 | 1207 | // Assuming this matrix has fourth column of 0,0,0,1 and isn't identity this function: 1208 | // Returns false if HasInverse is false, otherwise inverts the matrix. 1209 | 1210 | 1211 | // RETURNS true if has inverse & invert was done. Otherwise returns false & leaves matrix unchanged. 1212 | 1213 | 1214 | #endregion Internal Methods 1215 | 1216 | #region Private Methods 1217 | 1218 | private static Matrix3D CreateIdentity() 1219 | { 1220 | // Don't call this function, use s_identity. 1221 | Matrix3D matrix = new Matrix3D(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); 1222 | matrix.IsDistinguishedIdentity = true; 1223 | return matrix; 1224 | } 1225 | 1226 | #endregion Private Methods 1227 | 1228 | #region Private Properties 1229 | 1230 | // Returns true if this matrix is guaranteed to be the identity matrix. 1231 | // This is true when a new matrix has been created or after the Identity 1232 | // has already been computed. 1233 | // 1234 | // NOTE: In the case of a new matrix, the _m* fields on the diagonal 1235 | // will be uninitialized. You should either use the properties which interpret 1236 | // this state and return 1.0 or you can frequently early exit with a known 1237 | // value for the identity matrix. 1238 | // 1239 | // NOTE: This property being false does not mean that the matrix is 1240 | // not the identity matrix, it means that we do not know for certain if 1241 | // it is the identity matrix. Use the Identity property if you need to 1242 | // know if the matrix is the identity matrix. (The result will be cached 1243 | // and this property will start returning true.) 1244 | // 1245 | private bool IsDistinguishedIdentity 1246 | { 1247 | get 1248 | { 1249 | Debug.Assert( 1250 | _isNotKnownToBeIdentity 1251 | || ( 1252 | (_m11 == 0.0 || _m11 == 1.0) && (_m12 == 0.0) && (_m13 == 0.0) && (_m14 == 0.0) && 1253 | (_m21 == 0.0) && (_m22 == 0.0 || _m22 == 1.0) && (_m23 == 0.0) && (_m24 == 0.0) && 1254 | (_m31 == 0.0) && (_m32 == 0.0) && (_m33 == 0.0 || _m33 == 1.0) && (_m34 == 0.0) && 1255 | (_offsetX == 0.0) && (_offsetY == 0.0) && (_offsetZ == 0.0) && (_m44 == 0.0 || _m44 == 1.0)), 1256 | "Matrix3D.IsDistinguishedIdentity - _isNotKnownToBeIdentity flag is inconsistent with matrix state."); 1257 | 1258 | return !_isNotKnownToBeIdentity; 1259 | } 1260 | 1261 | set 1262 | { 1263 | _isNotKnownToBeIdentity = !value; 1264 | 1265 | // This not only verifies we got the inversion right, but we also hit the 1266 | // the assert above which verifies the value matches the state of the matrix. 1267 | Debug.Assert(IsDistinguishedIdentity == value, 1268 | "Matrix3D.IsDistinguishedIdentity - Error detected setting IsDistinguishedIdentity."); 1269 | } 1270 | } 1271 | 1272 | #endregion Private Properties 1273 | 1274 | private double _m11; 1275 | private double _m12; 1276 | private double _m13; 1277 | private double _m14; 1278 | 1279 | private double _m21; 1280 | private double _m22; 1281 | private double _m23; 1282 | private double _m24; 1283 | 1284 | private double _m31; 1285 | private double _m32; 1286 | private double _m33; 1287 | private double _m34; 1288 | 1289 | private double _offsetX; 1290 | private double _offsetY; 1291 | private double _offsetZ; 1292 | 1293 | private double _m44; 1294 | 1295 | // Internal matrix representation 1296 | private bool _isNotKnownToBeIdentity; 1297 | 1298 | // NOTE: The ctor used to create this identity sets the 1299 | // _isNotKnownToBeIdentity flag to true (i.e., s_identity 1300 | // is assumed not to be the identity by methods which 1301 | // early exit on the identity matrix.) 1302 | // 1303 | // For performance you should only use s_identity to 1304 | // initialize a matrix before writing new values. If 1305 | // you actually want an identity matrix you should use 1306 | // "new Matrix3D()" which takes advantage of the various 1307 | // opitimizations in the identity case. 1308 | // 1309 | private static readonly Matrix3D s_identity = CreateIdentity(); 1310 | 1311 | // The hash code for a matrix is the xor of its element's hashes. 1312 | // Since the identity matrix has 4 1's and 12 0's its hash is 0. 1313 | private const int c_identityHashCode = 0; 1314 | } 1315 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Media.Media3D/Point3D.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media.Media3D; 3 | 4 | using System; 5 | using System.Drawing; 6 | 7 | namespace System.Windows.Media.Media3D 8 | { 9 | /// 10 | /// Point3D - 3D point representation. 11 | /// Defaults to (0,0,0). 12 | /// 13 | public partial struct Point3D 14 | { 15 | //------------------------------------------------------ 16 | // 17 | // Constructors 18 | // 19 | //------------------------------------------------------ 20 | 21 | #region Constructors 22 | 23 | /// 24 | /// Constructor that sets point's initial values. 25 | /// 26 | /// Value of the X coordinate of the new point. 27 | /// Value of the Y coordinate of the new point. 28 | /// Value of the Z coordinate of the new point. 29 | public double X; 30 | public double Y; 31 | public double Z; 32 | public Point3D(double x, double y, double z) 33 | { 34 | X = x; 35 | Y = y; 36 | Z = z; 37 | } 38 | 39 | #endregion Constructors 40 | 41 | 42 | //------------------------------------------------------ 43 | // 44 | // Public Methods 45 | // 46 | //------------------------------------------------------ 47 | 48 | #region Public Methods 49 | 50 | /// 51 | /// Offset - update point position by adding offsetX to X, offsetY to Y, and offsetZ to Z. 52 | /// 53 | /// Offset in the X direction. 54 | /// Offset in the Y direction. 55 | /// Offset in the Z direction. 56 | public void Offset(double offsetX, double offsetY, double offsetZ) 57 | { 58 | X += offsetX; 59 | Y += offsetY; 60 | Z += offsetZ; 61 | } 62 | 63 | /// 64 | /// Point3D + Vector3D addition. 65 | /// 66 | /// Point being added. 67 | /// Vector being added. 68 | /// Result of addition. 69 | public static Point3D operator +(Point3D point, Vector3D vector) 70 | { 71 | return new Point3D(point.X + vector.X, 72 | point.Y + vector.Y, 73 | point.Z + vector.Z); 74 | } 75 | 76 | /// 77 | /// Point3D + Vector3D addition. 78 | /// 79 | /// Point being added. 80 | /// Vector being added. 81 | /// Result of addition. 82 | public static Point3D Add(Point3D point, Vector3D vector) 83 | { 84 | return new Point3D(point.X + vector.X, 85 | point.Y + vector.Y, 86 | point.Z + vector.Z); 87 | } 88 | 89 | /// 90 | /// Point3D - Vector3D subtraction. 91 | /// 92 | /// Point from which vector is being subtracted. 93 | /// Vector being subtracted from the point. 94 | /// Result of subtraction. 95 | public static Point3D operator -(Point3D point, Vector3D vector) 96 | { 97 | return new Point3D(point.X - vector.X, 98 | point.Y - vector.Y, 99 | point.Z - vector.Z); 100 | } 101 | 102 | /// 103 | /// Point3D - Vector3D subtraction. 104 | /// 105 | /// Point from which vector is being subtracted. 106 | /// Vector being subtracted from the point. 107 | /// Result of subtraction. 108 | public static Point3D Subtract(Point3D point, Vector3D vector) 109 | { 110 | return new Point3D(point.X - vector.X, 111 | point.Y - vector.Y, 112 | point.Z - vector.Z); 113 | } 114 | 115 | /// 116 | /// Subtraction. 117 | /// 118 | /// Point from which we are subtracting the second point. 119 | /// Point being subtracted. 120 | /// Vector between the two points. 121 | public static Vector3D operator -(Point3D point1, Point3D point2) 122 | { 123 | return new Vector3D(point1.X - point2.X, 124 | point1.Y - point2.Y, 125 | point1.Z - point2.Z); 126 | } 127 | 128 | /// 129 | /// Subtraction. 130 | /// 131 | /// Point from which we are subtracting the second point. 132 | /// Point being subtracted. 133 | /// Vector between the two points. 134 | public static Vector3D Subtract(Point3D point1, Point3D point2) 135 | { 136 | Vector3D v = new Vector3D(); 137 | Subtract(ref point1, ref point2, out v); 138 | return v; 139 | } 140 | 141 | /// 142 | /// Faster internal version of Subtract that avoids copies 143 | /// 144 | /// p1 and p2 to a passed by ref for perf and ARE NOT MODIFIED 145 | /// 146 | internal static void Subtract(ref Point3D p1, ref Point3D p2, out Vector3D result) 147 | { 148 | result.X = p1.X - p2.X; 149 | result.Y = p1.Y - p2.Y; 150 | result.Z = p1.Z - p2.Z; 151 | } 152 | 153 | /// 154 | /// Point3D * Matrix3D multiplication. 155 | /// 156 | /// Point being transformed. 157 | /// Transformation matrix applied to the point. 158 | /// Result of the transformation matrix applied to the point. 159 | public static Point3D operator *(Point3D point, Matrix3D matrix) 160 | { 161 | return matrix.Transform(point); 162 | } 163 | 164 | /// 165 | /// Point3D * Matrix3D multiplication. 166 | /// 167 | /// Point being transformed. 168 | /// Transformation matrix applied to the point. 169 | /// Result of the transformation matrix applied to the point. 170 | public static Point3D Multiply(Point3D point, Matrix3D matrix) 171 | { 172 | return matrix.Transform(point); 173 | } 174 | 175 | /// 176 | /// Explicit conversion to Vector3D. 177 | /// 178 | /// Given point. 179 | /// Vector representing the point. 180 | public static explicit operator Vector3D(Point3D point) 181 | { 182 | return new Vector3D(point.X, point.Y, point.Z); 183 | } 184 | 185 | /// 186 | /// Explicit conversion to Point4D. 187 | /// 188 | /// Given point. 189 | /// 4D point representing the 3D point. 190 | public static explicit operator Point4D(Point3D point) 191 | { 192 | return new Point4D(point.X, point.Y, point.Z, 1.0); 193 | } 194 | 195 | public static explicit operator Point(Point3D point) 196 | { 197 | return new Point((int)Math.Floor(point.X), (int)Math.Floor(point.Y)); 198 | } 199 | 200 | #endregion Public Methods 201 | } 202 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Media.Media3D/Point4D.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Windows; 3 | using System.Windows.Media.Media3D; 4 | using System; 5 | using System.Text; 6 | 7 | namespace System.Windows.Media.Media3D 8 | { 9 | public partial struct Point4D 10 | { 11 | public override string ToString() 12 | { 13 | var sb = new StringBuilder("[ "); 14 | sb.Append(X); 15 | sb.Append(" "); 16 | sb.Append(Y); 17 | sb.Append(" "); 18 | sb.Append(Z); 19 | sb.Append(" "); 20 | sb.Append(W); 21 | sb.Append(" ]"); 22 | 23 | return sb.ToString(); 24 | } 25 | 26 | //------------------------------------------------------ 27 | // 28 | // Constructors 29 | // 30 | //------------------------------------------------------ 31 | 32 | #region Constructors 33 | public double X; 34 | public double Y; 35 | public double Z; 36 | public double W; 37 | public Point4D(double x, double y, double z, double w) 38 | { 39 | X = x; 40 | Y = y; 41 | Z = z; 42 | W = w; 43 | } 44 | 45 | #endregion Constructors 46 | 47 | 48 | //------------------------------------------------------ 49 | // 50 | // Public Methods 51 | // 52 | //------------------------------------------------------ 53 | 54 | #region Public Methods 55 | 56 | public void Offset(double deltaX, double deltaY, double deltaZ, double deltaW) 57 | { 58 | X += deltaX; 59 | Y += deltaY; 60 | Z += deltaZ; 61 | W += deltaW; 62 | } 63 | 64 | public static Point4D operator +(Point4D point1, Point4D point2) 65 | { 66 | return new Point4D(point1.X + point2.X, 67 | point1.Y + point2.Y, 68 | point1.Z + point2.Z, 69 | point1.W + point2.W); 70 | } 71 | 72 | public static Point4D Add(Point4D point1, Point4D point2) 73 | { 74 | return new Point4D(point1.X + point2.X, 75 | point1.Y + point2.Y, 76 | point1.Z + point2.Z, 77 | point1.W + point2.W); 78 | } 79 | 80 | public static Point4D operator -(Point4D point1, Point4D point2) 81 | { 82 | return new Point4D(point1.X - point2.X, 83 | point1.Y - point2.Y, 84 | point1.Z - point2.Z, 85 | point1.W - point2.W); 86 | } 87 | 88 | public static Point4D Subtract(Point4D point1, Point4D point2) 89 | { 90 | return new Point4D(point1.X - point2.X, 91 | point1.Y - point2.Y, 92 | point1.Z - point2.Z, 93 | point1.W - point2.W); 94 | } 95 | 96 | public static Point4D operator *(Point4D point, Matrix3D matrix) 97 | { 98 | return matrix.Transform(point); 99 | } 100 | 101 | public static Point4D operator *(double factor, Point4D point) 102 | { 103 | return new Point4D(factor * point.X, 104 | factor * point.Y, 105 | factor * point.Z, 106 | factor * point.W); 107 | } 108 | 109 | public static Point4D Multiply(Point4D point, Matrix3D matrix) 110 | { 111 | return matrix.Transform(point); 112 | } 113 | 114 | public static explicit operator Point3D(Point4D point) 115 | { 116 | return new Point3D(point.X, point.Y, point.Z); 117 | } 118 | 119 | #endregion Public Methods 120 | } 121 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Media.Media3D/Quaternion.cs: -------------------------------------------------------------------------------- 1 |  2 | using MS.Internal; 3 | using System; 4 | using System.Collections; 5 | using System.ComponentModel; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | using System.ComponentModel.Design.Serialization; 11 | using System.Windows.Markup; 12 | using System.Windows.Media; 13 | // These types are aliased to match the unamanaged names used in interop 14 | using BOOL = System.Boolean; 15 | using WORD = System.UInt16; 16 | using Float = System.Single; 17 | 18 | namespace System.Windows.Media.Media3D 19 | { 20 | 21 | /// 22 | /// Quaternions. 23 | /// Quaternions are distinctly 3D entities that represent rotation in three dimensions. 24 | /// Their power comes in being able to interpolate (and thus animate) between 25 | /// quaternions to achieve a smooth, reliable interpolation. 26 | /// The default quaternion is the identity. 27 | /// 28 | public partial struct Quaternion 29 | { 30 | //------------------------------------------------------ 31 | // 32 | // Constructors 33 | // 34 | //------------------------------------------------------ 35 | 36 | #region Constructors 37 | 38 | /// 39 | /// Constructor that sets quaternion's initial values. 40 | /// 41 | /// Value of the X coordinate of the new quaternion. 42 | /// Value of the Y coordinate of the new quaternion. 43 | /// Value of the Z coordinate of the new quaternion. 44 | /// Value of the W coordinate of the new quaternion. 45 | public Quaternion(double x, double y, double z, double w) 46 | { 47 | _x = x; 48 | _y = y; 49 | _z = z; 50 | _w = w; 51 | _isNotDistinguishedIdentity = true; 52 | } 53 | 54 | /// 55 | /// Constructs a quaternion via specified axis of rotation and an angle. 56 | /// Throws an InvalidOperationException if given (0,0,0) as axis vector. 57 | /// 58 | /// Vector representing axis of rotation. 59 | /// Angle to turn around the given axis (in degrees). 60 | public Quaternion(Vector3D axisOfRotation, double angleInDegrees) 61 | { 62 | angleInDegrees %= 360.0; // Doing the modulo before converting to radians reduces total error 63 | double angleInRadians = angleInDegrees * (Math.PI / 180.0); 64 | double length = axisOfRotation.Length; 65 | //if (length == 0) 66 | // throw new System.InvalidOperationException(SR.Get(SRID.Quaternion_ZeroAxisSpecified)); 67 | Vector3D v = (axisOfRotation / length) * Math.Sin(0.5 * angleInRadians); 68 | _x = v.X; 69 | _y = v.Y; 70 | _z = v.Z; 71 | _w = Math.Cos(0.5 * angleInRadians); 72 | _isNotDistinguishedIdentity = true; 73 | } 74 | 75 | #endregion Constructors 76 | 77 | 78 | //------------------------------------------------------ 79 | // 80 | // Public Methods 81 | // 82 | //------------------------------------------------------ 83 | 84 | #region Public Methods 85 | /// 86 | /// Identity quaternion 87 | /// 88 | public static Quaternion Identity 89 | { 90 | get 91 | { 92 | return s_identity; 93 | } 94 | } 95 | 96 | /// 97 | /// Retrieves quaternion's axis. 98 | /// 99 | public Vector3D Axis 100 | { 101 | // q = M [cos(Q/2), sin(Q /2)v] 102 | // axis = sin(Q/2)v 103 | // angle = cos(Q/2) 104 | // M is magnitude 105 | get 106 | { 107 | // Handle identity (where axis is indeterminate) by 108 | // returning arbitrary axis. 109 | if (IsDistinguishedIdentity || (_x == 0 && _y == 0 && _z == 0)) 110 | { 111 | return new Vector3D(0, 1, 0); 112 | } 113 | else 114 | { 115 | Vector3D v = new Vector3D(_x, _y, _z); 116 | v.Normalize(); 117 | return v; 118 | } 119 | } 120 | } 121 | 122 | /// 123 | /// Retrieves quaternion's angle. 124 | /// 125 | public double Angle 126 | { 127 | get 128 | { 129 | if (IsDistinguishedIdentity) 130 | { 131 | return 0; 132 | } 133 | 134 | // Magnitude of quaternion times sine and cosine 135 | double msin = Math.Sqrt(_x * _x + _y * _y + _z * _z); 136 | double mcos = _w; 137 | 138 | if (!(msin <= Double.MaxValue)) 139 | { 140 | // Overflowed probably in squaring, so let's scale 141 | // the values. We don't need to include _w in the 142 | // scale factor because we're not going to square 143 | // it. 144 | double maxcoeff = Math.Max(Math.Abs(_x), Math.Max(Math.Abs(_y), Math.Abs(_z))); 145 | double x = _x / maxcoeff; 146 | double y = _y / maxcoeff; 147 | double z = _z / maxcoeff; 148 | msin = Math.Sqrt(x * x + y * y + z * z); 149 | // Scale mcos too. 150 | mcos = _w / maxcoeff; 151 | } 152 | 153 | // Atan2 is better than acos. (More precise and more efficient.) 154 | return Math.Atan2(msin, mcos) * (360.0 / Math.PI); 155 | } 156 | } 157 | 158 | /// 159 | /// Returns whether the quaternion is normalized (i.e. has a magnitude of 1). 160 | /// 161 | 162 | 163 | /// 164 | /// Tests whether or not a given quaternion is an identity quaternion. 165 | /// 166 | public bool IsIdentity 167 | { 168 | get 169 | { 170 | return IsDistinguishedIdentity || (_x == 0 && _y == 0 && _z == 0 && _w == 1); 171 | } 172 | } 173 | 174 | /// 175 | /// Relaces quaternion with its conjugate 176 | /// 177 | public void Conjugate() 178 | { 179 | if (IsDistinguishedIdentity) 180 | { 181 | return; 182 | } 183 | 184 | // Conjugate([x,y,z,w]) = [-x,-y,-z,w] 185 | _x = -_x; 186 | _y = -_y; 187 | _z = -_z; 188 | } 189 | 190 | /// 191 | /// Replaces quaternion with its inverse 192 | /// 193 | public void Invert() 194 | { 195 | if (IsDistinguishedIdentity) 196 | { 197 | return; 198 | } 199 | 200 | // Inverse = Conjugate / Norm Squared 201 | Conjugate(); 202 | double norm2 = _x * _x + _y * _y + _z * _z + _w * _w; 203 | _x /= norm2; 204 | _y /= norm2; 205 | _z /= norm2; 206 | _w /= norm2; 207 | } 208 | 209 | /// 210 | /// Normalizes this quaternion. 211 | /// 212 | public void Normalize() 213 | { 214 | if (IsDistinguishedIdentity) 215 | { 216 | return; 217 | } 218 | 219 | double norm2 = _x * _x + _y * _y + _z * _z + _w * _w; 220 | if (norm2 > Double.MaxValue) 221 | { 222 | // Handle overflow in computation of norm2 223 | double rmax = 1.0 / Max(Math.Abs(_x), 224 | Math.Abs(_y), 225 | Math.Abs(_z), 226 | Math.Abs(_w)); 227 | 228 | _x *= rmax; 229 | _y *= rmax; 230 | _z *= rmax; 231 | _w *= rmax; 232 | norm2 = _x * _x + _y * _y + _z * _z + _w * _w; 233 | } 234 | double normInverse = 1.0 / Math.Sqrt(norm2); 235 | _x *= normInverse; 236 | _y *= normInverse; 237 | _z *= normInverse; 238 | _w *= normInverse; 239 | } 240 | 241 | /// 242 | /// Quaternion addition. 243 | /// 244 | /// First quaternion being added. 245 | /// Second quaternion being added. 246 | /// Result of addition. 247 | public static Quaternion operator +(Quaternion left, Quaternion right) 248 | { 249 | if (right.IsDistinguishedIdentity) 250 | { 251 | if (left.IsDistinguishedIdentity) 252 | { 253 | return new Quaternion(0, 0, 0, 2); 254 | } 255 | else 256 | { 257 | // We know left is not distinguished identity here. 258 | left._w += 1; 259 | return left; 260 | } 261 | } 262 | else if (left.IsDistinguishedIdentity) 263 | { 264 | // We know right is not distinguished identity here. 265 | right._w += 1; 266 | return right; 267 | } 268 | else 269 | { 270 | return new Quaternion(left._x + right._x, 271 | left._y + right._y, 272 | left._z + right._z, 273 | left._w + right._w); 274 | } 275 | } 276 | 277 | /// 278 | /// Quaternion addition. 279 | /// 280 | /// First quaternion being added. 281 | /// Second quaternion being added. 282 | /// Result of addition. 283 | public static Quaternion Add(Quaternion left, Quaternion right) 284 | { 285 | return (left + right); 286 | } 287 | 288 | /// 289 | /// Quaternion subtraction. 290 | /// 291 | /// Quaternion to subtract from. 292 | /// Quaternion to subtract from the first quaternion. 293 | /// Result of subtraction. 294 | public static Quaternion operator -(Quaternion left, Quaternion right) 295 | { 296 | if (right.IsDistinguishedIdentity) 297 | { 298 | if (left.IsDistinguishedIdentity) 299 | { 300 | return new Quaternion(0, 0, 0, 0); 301 | } 302 | else 303 | { 304 | // We know left is not distinguished identity here. 305 | left._w -= 1; 306 | return left; 307 | } 308 | } 309 | else if (left.IsDistinguishedIdentity) 310 | { 311 | // We know right is not distinguished identity here. 312 | return new Quaternion(-right._x, -right._y, -right._z, 1 - right._w); 313 | } 314 | else 315 | { 316 | return new Quaternion(left._x - right._x, 317 | left._y - right._y, 318 | left._z - right._z, 319 | left._w - right._w); 320 | } 321 | } 322 | 323 | /// 324 | /// Quaternion subtraction. 325 | /// 326 | /// Quaternion to subtract from. 327 | /// Quaternion to subtract from the first quaternion. 328 | /// Result of subtraction. 329 | public static Quaternion Subtract(Quaternion left, Quaternion right) 330 | { 331 | return (left - right); 332 | } 333 | 334 | /// 335 | /// Quaternion multiplication. 336 | /// 337 | /// First quaternion. 338 | /// Second quaternion. 339 | /// Result of multiplication. 340 | public static Quaternion operator *(Quaternion left, Quaternion right) 341 | { 342 | if (left.IsDistinguishedIdentity) 343 | { 344 | return right; 345 | } 346 | if (right.IsDistinguishedIdentity) 347 | { 348 | return left; 349 | } 350 | 351 | double x = left._w * right._x + left._x * right._w + left._y * right._z - left._z * right._y; 352 | double y = left._w * right._y + left._y * right._w + left._z * right._x - left._x * right._z; 353 | double z = left._w * right._z + left._z * right._w + left._x * right._y - left._y * right._x; 354 | double w = left._w * right._w - left._x * right._x - left._y * right._y - left._z * right._z; 355 | Quaternion result = new Quaternion(x, y, z, w); 356 | return result; 357 | 358 | } 359 | 360 | /// 361 | /// Quaternion multiplication. 362 | /// 363 | /// First quaternion. 364 | /// Second quaternion. 365 | /// Result of multiplication. 366 | public static Quaternion Multiply(Quaternion left, Quaternion right) 367 | { 368 | return left * right; 369 | } 370 | 371 | /// 372 | /// Scale this quaternion by a scalar. 373 | /// 374 | /// Value to scale by. 375 | private void Scale(double scale) 376 | { 377 | if (IsDistinguishedIdentity) 378 | { 379 | _w = scale; 380 | IsDistinguishedIdentity = false; 381 | return; 382 | } 383 | _x *= scale; 384 | _y *= scale; 385 | _z *= scale; 386 | _w *= scale; 387 | } 388 | 389 | /// 390 | /// Return length of quaternion. 391 | /// 392 | private double Length() 393 | { 394 | if (IsDistinguishedIdentity) 395 | { 396 | return 1; 397 | } 398 | 399 | double norm2 = _x * _x + _y * _y + _z * _z + _w * _w; 400 | if (!(norm2 <= Double.MaxValue)) 401 | { 402 | // Do this the slow way to avoid squaring large 403 | // numbers since the length of many quaternions is 404 | // representable even if the squared length isn't. Of 405 | // course some lengths aren't representable because 406 | // the length can be up to twice as big as the largest 407 | // coefficient. 408 | 409 | double max = Math.Max(Math.Max(Math.Abs(_x), Math.Abs(_y)), 410 | Math.Max(Math.Abs(_z), Math.Abs(_w))); 411 | 412 | double x = _x / max; 413 | double y = _y / max; 414 | double z = _z / max; 415 | double w = _w / max; 416 | 417 | double smallLength = Math.Sqrt(x * x + y * y + z * z + w * w); 418 | // Return length of this smaller vector times the scale we applied originally. 419 | return smallLength * max; 420 | } 421 | return Math.Sqrt(norm2); 422 | } 423 | 424 | /// 425 | /// Smoothly interpolate between the two given quaternions using Spherical 426 | /// Linear Interpolation (SLERP). 427 | /// 428 | /// First quaternion for interpolation. 429 | /// Second quaternion for interpolation. 430 | /// Interpolation coefficient. 431 | /// SLERP-interpolated quaternion between the two given quaternions. 432 | public static Quaternion Slerp(Quaternion from, Quaternion to, double t) 433 | { 434 | return Slerp(from, to, t, /* useShortestPath = */ true); 435 | } 436 | 437 | /// 438 | /// Smoothly interpolate between the two given quaternions using Spherical 439 | /// Linear Interpolation (SLERP). 440 | /// 441 | /// First quaternion for interpolation. 442 | /// Second quaternion for interpolation. 443 | /// Interpolation coefficient. 444 | /// If true, Slerp will automatically flip the sign of 445 | /// the destination Quaternion to ensure the shortest path is taken. 446 | /// SLERP-interpolated quaternion between the two given quaternions. 447 | public static Quaternion Slerp(Quaternion from, Quaternion to, double t, bool useShortestPath) 448 | { 449 | if (from.IsDistinguishedIdentity) 450 | { 451 | from._w = 1; 452 | } 453 | if (to.IsDistinguishedIdentity) 454 | { 455 | to._w = 1; 456 | } 457 | 458 | double cosOmega; 459 | double scaleFrom, scaleTo; 460 | 461 | // Normalize inputs and stash their lengths 462 | double lengthFrom = from.Length(); 463 | double lengthTo = to.Length(); 464 | from.Scale(1 / lengthFrom); 465 | to.Scale(1 / lengthTo); 466 | 467 | // Calculate cos of omega. 468 | cosOmega = from._x * to._x + from._y * to._y + from._z * to._z + from._w * to._w; 469 | 470 | if (useShortestPath) 471 | { 472 | // If we are taking the shortest path we flip the signs to ensure that 473 | // cosOmega will be positive. 474 | if (cosOmega < 0.0) 475 | { 476 | cosOmega = -cosOmega; 477 | to._x = -to._x; 478 | to._y = -to._y; 479 | to._z = -to._z; 480 | to._w = -to._w; 481 | } 482 | } 483 | else 484 | { 485 | // If we are not taking the UseShortestPath we clamp cosOmega to 486 | // -1 to stay in the domain of Math.Acos below. 487 | if (cosOmega < -1.0) 488 | { 489 | cosOmega = -1.0; 490 | } 491 | } 492 | 493 | // Clamp cosOmega to [-1,1] to stay in the domain of Math.Acos below. 494 | // The logic above has either flipped the sign of cosOmega to ensure it 495 | // is positive or clamped to -1 aready. We only need to worry about the 496 | // upper limit here. 497 | if (cosOmega > 1.0) 498 | { 499 | cosOmega = 1.0; 500 | } 501 | 502 | Debug.Assert(!(cosOmega < -1.0) && !(cosOmega > 1.0), 503 | "cosOmega should be clamped to [-1,1]"); 504 | 505 | // The mainline algorithm doesn't work for extreme 506 | // cosine values. For large cosine we have a better 507 | // fallback hence the asymmetric limits. 508 | const double maxCosine = 1.0 - 1e-6; 509 | const double minCosine = 1e-10 - 1.0; 510 | 511 | // Calculate scaling coefficients. 512 | if (cosOmega > maxCosine) 513 | { 514 | // Quaternions are too close - use linear interpolation. 515 | scaleFrom = 1.0 - t; 516 | scaleTo = t; 517 | } 518 | else if (cosOmega < minCosine) 519 | { 520 | // Quaternions are nearly opposite, so we will pretend to 521 | // is exactly -from. 522 | // First assign arbitrary perpendicular to "to". 523 | to = new Quaternion(-from.Y, from.X, -from.W, from.Z); 524 | 525 | double theta = t * Math.PI; 526 | 527 | scaleFrom = Math.Cos(theta); 528 | scaleTo = Math.Sin(theta); 529 | } 530 | else 531 | { 532 | // Standard case - use SLERP interpolation. 533 | double omega = Math.Acos(cosOmega); 534 | double sinOmega = Math.Sqrt(1.0 - cosOmega * cosOmega); 535 | scaleFrom = Math.Sin((1.0 - t) * omega) / sinOmega; 536 | scaleTo = Math.Sin(t * omega) / sinOmega; 537 | } 538 | 539 | // We want the magnitude of the output quaternion to be 540 | // multiplicatively interpolated between the input 541 | // magnitudes, i.e. lengthOut = lengthFrom * (lengthTo/lengthFrom)^t 542 | // = lengthFrom ^ (1-t) * lengthTo ^ t 543 | 544 | double lengthOut = lengthFrom * Math.Pow(lengthTo / lengthFrom, t); 545 | scaleFrom *= lengthOut; 546 | scaleTo *= lengthOut; 547 | 548 | return new Quaternion(scaleFrom * from._x + scaleTo * to._x, 549 | scaleFrom * from._y + scaleTo * to._y, 550 | scaleFrom * from._z + scaleTo * to._z, 551 | scaleFrom * from._w + scaleTo * to._w); 552 | } 553 | 554 | #endregion Public Methods 555 | 556 | #region Private Methods 557 | 558 | static private double Max(double a, double b, double c, double d) 559 | { 560 | if (b > a) 561 | a = b; 562 | if (c > a) 563 | a = c; 564 | if (d > a) 565 | a = d; 566 | return a; 567 | } 568 | 569 | #endregion Private Methods 570 | 571 | //------------------------------------------------------ 572 | // 573 | // Public Properties 574 | // 575 | //------------------------------------------------------ 576 | 577 | #region Public Properties 578 | 579 | /// 580 | /// X - Default value is 0. 581 | /// 582 | public double X 583 | { 584 | get 585 | { 586 | return _x; 587 | } 588 | 589 | set 590 | { 591 | if (IsDistinguishedIdentity) 592 | { 593 | this = s_identity; 594 | IsDistinguishedIdentity = false; 595 | } 596 | _x = value; 597 | } 598 | } 599 | 600 | /// 601 | /// Y - Default value is 0. 602 | /// 603 | public double Y 604 | { 605 | get 606 | { 607 | return _y; 608 | } 609 | 610 | set 611 | { 612 | if (IsDistinguishedIdentity) 613 | { 614 | this = s_identity; 615 | IsDistinguishedIdentity = false; 616 | } 617 | _y = value; 618 | } 619 | } 620 | 621 | /// 622 | /// Z - Default value is 0. 623 | /// 624 | public double Z 625 | { 626 | get 627 | { 628 | return _z; 629 | } 630 | 631 | set 632 | { 633 | if (IsDistinguishedIdentity) 634 | { 635 | this = s_identity; 636 | IsDistinguishedIdentity = false; 637 | } 638 | _z = value; 639 | } 640 | } 641 | 642 | /// 643 | /// W - Default value is 1. 644 | /// 645 | public double W 646 | { 647 | get 648 | { 649 | if (IsDistinguishedIdentity) 650 | { 651 | return 1.0; 652 | } 653 | else 654 | { 655 | return _w; 656 | } 657 | } 658 | 659 | set 660 | { 661 | if (IsDistinguishedIdentity) 662 | { 663 | this = s_identity; 664 | IsDistinguishedIdentity = false; 665 | } 666 | _w = value; 667 | } 668 | } 669 | 670 | #endregion Public Properties 671 | 672 | //------------------------------------------------------ 673 | // 674 | // Internal Fields 675 | // 676 | //------------------------------------------------------ 677 | 678 | #region Internal Fields 679 | 680 | internal double _x; 681 | internal double _y; 682 | internal double _z; 683 | internal double _w; 684 | 685 | #endregion Internal Fields 686 | 687 | #region Private Fields and Properties 688 | 689 | // If this bool is false then we are a default quaternion with 690 | // all doubles equal to zero, but should be treated as 691 | // identity. 692 | private bool _isNotDistinguishedIdentity; 693 | 694 | private bool IsDistinguishedIdentity 695 | { 696 | get 697 | { 698 | return !_isNotDistinguishedIdentity; 699 | } 700 | set 701 | { 702 | _isNotDistinguishedIdentity = !value; 703 | } 704 | } 705 | 706 | private static int GetIdentityHashCode() 707 | { 708 | // This code is called only once. 709 | double zero = 0; 710 | double one = 1; 711 | // return zero.GetHashCode() ^ zero.GetHashCode() ^ zero.GetHashCode() ^ one.GetHashCode(); 712 | // But this expression can be simplified because the first two hash codes cancel. 713 | return zero.GetHashCode() ^ one.GetHashCode(); 714 | } 715 | 716 | private static Quaternion GetIdentity() 717 | { 718 | // This code is called only once. 719 | Quaternion q = new Quaternion(0, 0, 0, 1); 720 | q.IsDistinguishedIdentity = true; 721 | return q; 722 | } 723 | 724 | 725 | // Hash code for identity. 726 | private static int c_identityHashCode = GetIdentityHashCode(); 727 | 728 | // Default identity 729 | private static Quaternion s_identity = GetIdentity(); 730 | 731 | #endregion Private Fields and Properties 732 | } 733 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Media.Media3D/Vector3D.cs: -------------------------------------------------------------------------------- 1 | using MS.Internal; 2 | using System; 3 | using System.Windows; 4 | using System.Windows.Media.Media3D; 5 | 6 | namespace System.Windows.Media.Media3D 7 | { 8 | /// 9 | /// Vector3D - 3D vector representation. 10 | /// 11 | public partial struct Vector3D 12 | { 13 | //------------------------------------------------------ 14 | // 15 | // Constructors 16 | // 17 | //------------------------------------------------------ 18 | 19 | #region Constructors 20 | 21 | /// 22 | /// Constructor that sets vector's initial values. 23 | /// 24 | /// Value of the X coordinate of the new vector. 25 | /// Value of the Y coordinate of the new vector. 26 | /// Value of the Z coordinate of the new vector. 27 | /// 28 | public double X; 29 | public double Y; 30 | public double Z; 31 | public Vector3D(double x, double y, double z) 32 | { 33 | X = x; 34 | Y = y; 35 | Z = z; 36 | } 37 | 38 | #endregion Constructors 39 | 40 | 41 | //------------------------------------------------------ 42 | // 43 | // Public Methods 44 | // 45 | //------------------------------------------------------ 46 | 47 | #region Public Methods 48 | 49 | /// 50 | /// Length of the vector. 51 | /// 52 | public double Length 53 | { 54 | get 55 | { 56 | return Math.Sqrt(X * X + Y * Y + Z * Z); 57 | } 58 | } 59 | 60 | /// 61 | /// Length of the vector squared. 62 | /// 63 | public double LengthSquared 64 | { 65 | get 66 | { 67 | return X * X + Y * Y + Z * Z; 68 | } 69 | } 70 | 71 | /// 72 | /// Updates the vector to maintain its direction, but to have a length 73 | /// of 1. Equivalent to dividing the vector by its Length. 74 | /// Returns NaN if length is zero. 75 | /// 76 | public void Normalize() 77 | { 78 | // Computation of length can overflow easily because it 79 | // first computes squared length, so we first divide by 80 | // the largest coefficient. 81 | double m = Math.Abs(X); 82 | double absy = Math.Abs(Y); 83 | double absz = Math.Abs(Z); 84 | if (absy > m) 85 | { 86 | m = absy; 87 | } 88 | if (absz > m) 89 | { 90 | m = absz; 91 | } 92 | 93 | X /= m; 94 | Y /= m; 95 | Z /= m; 96 | 97 | double length = Math.Sqrt(X * X + Y * Y + Z * Z); 98 | this /= length; 99 | } 100 | 101 | /// 102 | /// Computes the angle between two vectors. 103 | /// 104 | /// First vector. 105 | /// Second vector. 106 | /// 107 | /// Returns the angle required to rotate vector1 into vector2 in degrees. 108 | /// This will return a value between [0, 180] degrees. 109 | /// (Note that this is slightly different from the Vector member 110 | /// function of the same name. Signed angles do not extend to 3D.) 111 | /// 112 | 113 | 114 | /// 115 | /// Operator -Vector (unary negation). 116 | /// 117 | /// Vector being negated. 118 | /// Negation of the given vector. 119 | public static Vector3D operator -(Vector3D vector) 120 | { 121 | return new Vector3D(-vector.X, -vector.Y, -vector.Z); 122 | } 123 | 124 | /// 125 | /// Negates the values of X, Y, and Z on this Vector3D 126 | /// 127 | public void Negate() 128 | { 129 | X = -X; 130 | Y = -Y; 131 | Z = -Z; 132 | } 133 | 134 | /// 135 | /// Vector addition. 136 | /// 137 | /// First vector being added. 138 | /// Second vector being added. 139 | /// Result of addition. 140 | public static Vector3D operator +(Vector3D vector1, Vector3D vector2) 141 | { 142 | return new Vector3D(vector1.X + vector2.X, 143 | vector1.Y + vector2.Y, 144 | vector1.Z + vector2.Z); 145 | } 146 | 147 | /// 148 | /// Vector addition. 149 | /// 150 | /// First vector being added. 151 | /// Second vector being added. 152 | /// Result of addition. 153 | public static Vector3D Add(Vector3D vector1, Vector3D vector2) 154 | { 155 | return new Vector3D(vector1.X + vector2.X, 156 | vector1.Y + vector2.Y, 157 | vector1.Z + vector2.Z); 158 | } 159 | 160 | /// 161 | /// Vector subtraction. 162 | /// 163 | /// Vector that is subtracted from. 164 | /// Vector being subtracted. 165 | /// Result of subtraction. 166 | public static Vector3D operator -(Vector3D vector1, Vector3D vector2) 167 | { 168 | return new Vector3D(vector1.X - vector2.X, 169 | vector1.Y - vector2.Y, 170 | vector1.Z - vector2.Z); 171 | } 172 | 173 | /// 174 | /// Vector subtraction. 175 | /// 176 | /// Vector that is subtracted from. 177 | /// Vector being subtracted. 178 | /// Result of subtraction. 179 | public static Vector3D Subtract(Vector3D vector1, Vector3D vector2) 180 | { 181 | return new Vector3D(vector1.X - vector2.X, 182 | vector1.Y - vector2.Y, 183 | vector1.Z - vector2.Z); 184 | } 185 | 186 | /// 187 | /// Vector3D + Point3D addition. 188 | /// 189 | /// Vector by which we offset the point. 190 | /// Point being offset by the given vector. 191 | /// Result of addition. 192 | public static Point3D operator +(Vector3D vector, Point3D point) 193 | { 194 | return new Point3D(vector.X + point.X, 195 | vector.Y + point.Y, 196 | vector.Z + point.Z); 197 | } 198 | 199 | /// 200 | /// Vector3D + Point3D addition. 201 | /// 202 | /// Vector by which we offset the point. 203 | /// Point being offset by the given vector. 204 | /// Result of addition. 205 | public static Point3D Add(Vector3D vector, Point3D point) 206 | { 207 | return new Point3D(vector.X + point.X, 208 | vector.Y + point.Y, 209 | vector.Z + point.Z); 210 | } 211 | 212 | /// 213 | /// Vector3D - Point3D subtraction. 214 | /// 215 | /// Vector by which we offset the point. 216 | /// Point being offset by the given vector. 217 | /// Result of subtraction. 218 | public static Point3D operator -(Vector3D vector, Point3D point) 219 | { 220 | return new Point3D(vector.X - point.X, 221 | vector.Y - point.Y, 222 | vector.Z - point.Z); 223 | } 224 | 225 | /// 226 | /// Vector3D - Point3D subtraction. 227 | /// 228 | /// Vector by which we offset the point. 229 | /// Point being offset by the given vector. 230 | /// Result of subtraction. 231 | public static Point3D Subtract(Vector3D vector, Point3D point) 232 | { 233 | return new Point3D(vector.X - point.X, 234 | vector.Y - point.Y, 235 | vector.Z - point.Z); 236 | } 237 | 238 | /// 239 | /// Scalar multiplication. 240 | /// 241 | /// Vector being multiplied. 242 | /// Scalar value by which the vector is multiplied. 243 | /// Result of multiplication. 244 | public static Vector3D operator *(Vector3D vector, double scalar) 245 | { 246 | return new Vector3D(vector.X * scalar, 247 | vector.Y * scalar, 248 | vector.Z * scalar); 249 | } 250 | 251 | /// 252 | /// Scalar multiplication. 253 | /// 254 | /// Vector being multiplied. 255 | /// Scalar value by which the vector is multiplied. 256 | /// Result of multiplication. 257 | public static Vector3D Multiply(Vector3D vector, double scalar) 258 | { 259 | return new Vector3D(vector.X * scalar, 260 | vector.Y * scalar, 261 | vector.Z * scalar); 262 | } 263 | 264 | /// 265 | /// Scalar multiplication. 266 | /// 267 | /// Scalar value by which the vector is multiplied 268 | /// Vector being multiplied. 269 | /// Result of multiplication. 270 | public static Vector3D operator *(double scalar, Vector3D vector) 271 | { 272 | return new Vector3D(vector.X * scalar, 273 | vector.Y * scalar, 274 | vector.Z * scalar); 275 | } 276 | 277 | /// 278 | /// Scalar multiplication. 279 | /// 280 | /// Scalar value by which the vector is multiplied 281 | /// Vector being multiplied. 282 | /// Result of multiplication. 283 | public static Vector3D Multiply(double scalar, Vector3D vector) 284 | { 285 | return new Vector3D(vector.X * scalar, 286 | vector.Y * scalar, 287 | vector.Z * scalar); 288 | } 289 | 290 | /// 291 | /// Scalar division. 292 | /// 293 | /// Vector being divided. 294 | /// Scalar value by which we divide the vector. 295 | /// Result of division. 296 | public static Vector3D operator /(Vector3D vector, double scalar) 297 | { 298 | return vector * (1.0 / scalar); 299 | } 300 | 301 | /// 302 | /// Scalar division. 303 | /// 304 | /// Vector being divided. 305 | /// Scalar value by which we divide the vector. 306 | /// Result of division. 307 | public static Vector3D Divide(Vector3D vector, double scalar) 308 | { 309 | return vector * (1.0 / scalar); 310 | } 311 | 312 | /// 313 | /// Vector3D * Matrix3D multiplication 314 | /// 315 | /// Vector being tranformed. 316 | /// Transformation matrix applied to the vector. 317 | /// Result of multiplication. 318 | public static Vector3D operator *(Vector3D vector, Matrix3D matrix) 319 | { 320 | return matrix.Transform(vector); 321 | } 322 | 323 | /// 324 | /// Vector3D * Matrix3D multiplication 325 | /// 326 | /// Vector being tranformed. 327 | /// Transformation matrix applied to the vector. 328 | /// Result of multiplication. 329 | public static Vector3D Multiply(Vector3D vector, Matrix3D matrix) 330 | { 331 | return matrix.Transform(vector); 332 | } 333 | 334 | /// 335 | /// Vector dot product. 336 | /// 337 | /// First vector. 338 | /// Second vector. 339 | /// Dot product of two vectors. 340 | public static double DotProduct(Vector3D vector1, Vector3D vector2) 341 | { 342 | return DotProduct(ref vector1, ref vector2); 343 | } 344 | 345 | /// 346 | /// Faster internal version of DotProduct that avoids copies 347 | /// 348 | /// vector1 and vector2 to a passed by ref for perf and ARE NOT MODIFIED 349 | /// 350 | internal static double DotProduct(ref Vector3D vector1, ref Vector3D vector2) 351 | { 352 | return vector1.X * vector2.X + 353 | vector1.Y * vector2.Y + 354 | vector1.Z * vector2.Z; 355 | } 356 | 357 | /// 358 | /// Vector cross product. 359 | /// 360 | /// First vector. 361 | /// Second vector. 362 | /// Cross product of two vectors. 363 | public static Vector3D CrossProduct(Vector3D vector1, Vector3D vector2) 364 | { 365 | Vector3D result; 366 | CrossProduct(ref vector1, ref vector2, out result); 367 | return result; 368 | } 369 | 370 | /// 371 | /// Faster internal version of CrossProduct that avoids copies 372 | /// 373 | /// vector1 and vector2 to a passed by ref for perf and ARE NOT MODIFIED 374 | /// 375 | internal static void CrossProduct(ref Vector3D vector1, ref Vector3D vector2, out Vector3D result) 376 | { 377 | result.X = vector1.Y * vector2.Z - vector1.Z * vector2.Y; 378 | result.Y = vector1.Z * vector2.X - vector1.X * vector2.Z; 379 | result.Z = vector1.X * vector2.Y - vector1.Y * vector2.X; 380 | } 381 | 382 | /// 383 | /// Vector3D to Point3D conversion. 384 | /// 385 | /// Vector being converted. 386 | /// Point representing the given vector. 387 | public static explicit operator Point3D(Vector3D vector) 388 | { 389 | return new Point3D(vector.X, vector.Y, vector.Z); 390 | } 391 | 392 | /// 393 | /// Explicit conversion to Size3D. Note that since Size3D cannot contain negative values, 394 | /// the resulting size will contains the absolute values of X, Y, and Z. 395 | /// 396 | /// The vector to convert to a size. 397 | /// A size equal to this vector. 398 | 399 | 400 | #endregion Public Methods 401 | } 402 | } -------------------------------------------------------------------------------- /winforms-z-buffer/Utils/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace winforms_z_buffer 8 | { 9 | public static class Ut 10 | { 11 | public static int F(double a) 12 | { 13 | return (int)Math.Floor(a); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /winforms-z-buffer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /winforms-z-buffer/winforms-z-buffer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {80C2B2E4-5216-4E26-9725-CDCA55DE81D8} 8 | WinExe 9 | winforms_z_buffer 10 | winforms-z-buffer 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Form 55 | 56 | 57 | Display.cs 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Display.cs 77 | 78 | 79 | ResXFileCodeGenerator 80 | Resources.Designer.cs 81 | Designer 82 | 83 | 84 | True 85 | Resources.resx 86 | 87 | 88 | 89 | SettingsSingleFileGenerator 90 | Settings.Designer.cs 91 | 92 | 93 | True 94 | Settings.settings 95 | True 96 | 97 | 98 | 99 | 100 | 101 | 102 | --------------------------------------------------------------------------------