├── .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 |
--------------------------------------------------------------------------------