Compare commits
2249 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a0f1c4126 | |||
| a792a0e7ea | |||
| cad85c8a7a | |||
| c40d25df29 | |||
| 5d4eab87f8 | |||
| d3ee4b1eda | |||
| b331a3b3fd | |||
| 0ec0b63ad2 | |||
| 659c2184b3 | |||
| 6bbd4defaf | |||
| ea4867d5b1 | |||
| 8d14b4798e | |||
| 1283747b59 | |||
| a2df815ba6 | |||
| cd7d6d470b | |||
| e8ef3c14d4 | |||
| 3a8ca47fce | |||
| 176607db25 | |||
| c389c2f6e4 | |||
| 144de5dd78 | |||
| 139ca8f6ef | |||
| 741602c9cc | |||
| 825561d6f4 | |||
| d932043905 | |||
| 68d613ef0d | |||
| 6ffcddcb87 | |||
| e9ece451af | |||
| 5d8db7fa96 | |||
| 608de9970b | |||
| 97de56756f | |||
| 854f882090 | |||
| 1119c1e68f | |||
| c5bc58ee46 | |||
| 0cd99fc3b3 | |||
| dadec3bd25 | |||
| 6ac1bae539 | |||
| 068076a2c2 | |||
| 51fe856360 | |||
| c719b4f3c3 | |||
| f7f45dc4c8 | |||
| dfbafdc3fb | |||
| 2d62eab4b4 | |||
| 4478c09544 | |||
| 438cdafd60 | |||
| 53b6c1f39a | |||
| 0c540ebea6 | |||
| 269e88b18d | |||
| 9a86def109 | |||
| faa6582adc | |||
| 77bba5e9f9 | |||
| bbfd2fb66c | |||
| ec37f295c7 | |||
| af44f2c720 | |||
| 913a9dcdc3 | |||
| 62b264f770 | |||
| edc2938fb8 | |||
| 2498414e91 | |||
| 3c5929258b | |||
| 810a01b779 | |||
| ad2957d534 | |||
| 6742f54243 | |||
| 5437f43790 | |||
| fc14556d43 | |||
| 4bd4c43d1a | |||
| f27e85eeb0 | |||
| 16a70dd267 | |||
| a53caab12b | |||
| 2cead53fc9 | |||
| 77defa69c6 | |||
| 8c540c8b83 | |||
| 0a415fad36 | |||
| 95bb3d6bb1 | |||
| 3142172cff | |||
| 074a265166 | |||
| ce3943446c | |||
| 6c0c2b4365 | |||
| 2fc8a05aec | |||
| 3eab6c5af2 | |||
| d1bd519e7c | |||
| 0b62184c51 | |||
| 7ae4bc208a | |||
| c28fbe566d | |||
| 5ae8d9b220 | |||
| 4d3ac11467 | |||
| 5777523b35 | |||
| e0eb666f6c | |||
| 19ebe3feb8 | |||
| b9e9480e5c | |||
| 70b7131ae7 | |||
| 7ec2c004a2 | |||
| 7e884e1eb1 | |||
| 7689d48632 | |||
| 514c6c11ae | |||
| e18828bed7 | |||
| 0d52d9aa52 | |||
| bf9db2148c | |||
| 768f7c0e45 | |||
| d160459f4a | |||
| 69b16484ec | |||
| 5f023e1e25 | |||
| b7996a4ad4 | |||
| 447bb98480 | |||
| 68d43a49df | |||
| 3353297858 | |||
| f56e4fe1a4 | |||
| 5a6e6b2733 | |||
| 58b96efe06 | |||
| 0328cfe83e | |||
| 4270fece64 | |||
| 9cba0c0b02 | |||
| 64201d49f0 | |||
| c801fcd835 | |||
| aac9946aef | |||
| ebdddf0bc0 | |||
| a8ac725564 | |||
| 342684587a | |||
| 9a532f2cda | |||
| 909b7fb425 | |||
| 0913c5e167 | |||
| 9abad0063e | |||
| 4b851f9b98 | |||
| a8339496e5 | |||
| 9a183196a2 | |||
| 5a12aa9e1b | |||
| 5c0d62ac2e | |||
| 3d511efbb9 | |||
| 05a159d648 | |||
| 5a9aa812e2 | |||
| 35a9deb301 | |||
| c0ed445918 | |||
| f954ace66b | |||
| 6776f8a79f | |||
| ba345cb823 | |||
| 39abd88a54 | |||
| ecccdf0279 | |||
| 095e4a8799 | |||
| a66ae2f51b | |||
| 781c6449d3 | |||
| eca952b8c5 | |||
| 13d4ee03b3 | |||
| 22f9af6bf3 | |||
| d01c071d25 | |||
| 3f4bf9062c | |||
| 92e0d7bab2 | |||
| d9174a110b | |||
| f9c92b7c78 | |||
| bb87cd5fba | |||
| 5d21c2d300 | |||
| da08a4129e | |||
| bff99dcf73 | |||
| 1d7538a557 | |||
| e483be7726 | |||
| 3b8a34bf25 | |||
| 0f6dfb782e | |||
| c8aada9941 | |||
| 3d4fa72b51 | |||
| 936ffcaabc | |||
| 84721d045e | |||
| 0443c73aab | |||
| 5778d74fbb | |||
| b67faf780e | |||
| 3afbd8f2e1 | |||
| fb771bf797 | |||
| 46f42ba9c8 | |||
| bee41d9491 | |||
| f37fb67e5a | |||
| 940fc985e7 | |||
| 384abca49e | |||
| c04eb822b7 | |||
| 0a51662c0b | |||
| 84af2eb8e1 | |||
| f3ef0fcf57 | |||
| 59f450bcfd | |||
| 53db1d2c9b | |||
| 8cc9b69017 | |||
| 12098fe113 | |||
| 53053d0693 | |||
| 829629d05f | |||
| 49233429e4 | |||
| 47b6dd34af | |||
| 4bd71d676f | |||
| 9a860c355a | |||
| c42a473521 | |||
| 43eb124964 | |||
| 86a8bbee9a | |||
| 4897ea8c35 | |||
| fa2edf8547 | |||
| dcefaf675c | |||
| 82b2558d96 | |||
| 466ecca932 | |||
| 96adde2d52 | |||
| b2a71b505b | |||
| ecafc030ef | |||
| 6007dca4d1 | |||
| 08ecc5005f | |||
| 7b70f2835c | |||
| d8b046a361 | |||
| 9ee683e971 | |||
| 0b01c898f0 | |||
| 08e6ef95ab | |||
| dd89260db3 | |||
| 534a04206b | |||
| 25eaf50586 | |||
| e259a150fb | |||
| 6bc8d6a231 | |||
| 8122846078 | |||
| 8fbdc6a50d | |||
| 2be3ebd401 | |||
| f114bed0d2 | |||
| 31deb4d25d | |||
| 413b776194 | |||
| af18baba7b | |||
| 527695bf53 | |||
| bcd3470b20 | |||
| fd86b347e6 | |||
| 801d72c55a | |||
| 6ea28cb69d | |||
| 19196bede7 | |||
| eed87fc699 | |||
| 6c1a67684e | |||
| e5b6055e7d | |||
| 3ea78d07b1 | |||
| c8c06ff230 | |||
| bbc596ad6c | |||
| 0930828a81 | |||
| 8c71406615 | |||
| 1b01dba827 | |||
| 4aa52ddd60 | |||
| b80de21136 | |||
| ca927e4227 | |||
| f2c17a64fb | |||
| c793624195 | |||
| 42d54163c5 | |||
| 5f2f80f480 | |||
| 88be1fee15 | |||
| 0a6a44522a | |||
| 0ba54b4b7c | |||
| 56b35ef6fb | |||
| 03855be6f9 | |||
| 39b55e20c0 | |||
| 24ab8e0b52 | |||
| a961938716 | |||
| 5c2b9d6011 | |||
| 66def25eab | |||
| 16b7338a59 | |||
| 5b89f42738 | |||
| 882a0bcb7d | |||
| 629d397508 | |||
| 346384f8fc | |||
| fe4af49759 | |||
| 5370236743 | |||
| ee289e4a30 | |||
| 83ccbc7add | |||
| aa633bf2c7 | |||
| 621b92cd5e | |||
| 0d549ae203 | |||
| d944e67a69 | |||
| e2b13be7a0 | |||
| 8750485c6d | |||
| 1f9c1e6627 | |||
| 050e782df5 | |||
| 732f4856c6 | |||
| 27863c0bc9 | |||
| f0215a7009 | |||
| 3a06962045 | |||
| 6fffba5bb2 | |||
| 2ace8e0a5e | |||
| 99e99e5b60 | |||
| fc19e7dfdb | |||
| 2398fa4044 | |||
| aedfb6e6fa | |||
| c760a1c545 | |||
| bcb30ce933 | |||
| a4c3485440 | |||
| e8e223630a | |||
| 7b68bdce5f | |||
| 8884e47051 | |||
| 7c352e2038 | |||
| 2b20d3ef64 | |||
| 0d5a7f3224 | |||
| 4bc0cdd8b3 | |||
| 3511c95836 | |||
| 5456ada1bd | |||
| 1fa3ef37c3 | |||
| a5d8c49d75 | |||
| 0e69125217 | |||
| 046ecb794d | |||
| d61ebcf446 | |||
| cfb3c942d7 | |||
| e49a3275e2 | |||
| 30964aec70 | |||
| 4e39d09d1d | |||
| 162b70ce5d | |||
| 4b49d5d355 | |||
| 3e18a46758 | |||
| d6edc898c5 | |||
| 1dcaf135e5 | |||
| 25919e8393 | |||
| 1080a4e6f9 | |||
| b97769bf5c | |||
| d17c1c57be | |||
| fd1fc0f1d1 | |||
| eeddfcbec2 | |||
| fe2b9bd444 | |||
| 5ebb4b54d9 | |||
| cdd2967087 | |||
| e61505149a | |||
| 8dbe249bf9 | |||
| 38a0857a69 | |||
| 2b1b586251 | |||
| f36cdde500 | |||
| 6de1bfb64c | |||
| a5e82117cb | |||
| 2b7d04368a | |||
| a9dbdbc822 | |||
| 6528829796 | |||
| 3ad682a898 | |||
| 7aca2ee5be | |||
| 1c99f118ff | |||
| b49be74501 | |||
| 55f69da613 | |||
| ae7966a20c | |||
| 2e92fdf272 | |||
| cf9a3cdc38 | |||
| 70951375c9 | |||
| 6329a0070d | |||
| 29fae88610 | |||
| ee0c0fa6ba | |||
| 33822213e6 | |||
| cb05bedf08 | |||
| 4cf328c50b | |||
| f44eb0f8ba | |||
| 76c9029ec3 | |||
| 68e3516075 | |||
| 19ff8e95e5 | |||
| 4bf49bf6a6 | |||
| 0e377e43cc | |||
| f9f23806ed | |||
| 3569e16519 | |||
| 2a40ddd5f9 | |||
| efcd8987d9 | |||
| e66a4ce337 | |||
| 6d19d7c257 | |||
| 988aee863c | |||
| 8253899a70 | |||
| 4edca43531 | |||
| a876fc788d | |||
| cc79e5ca83 | |||
| 8aeb204c8a | |||
| db73939504 | |||
| de90187eb0 | |||
| 8aa809d2bc | |||
| 9e28d91879 | |||
| 2e89516b6e | |||
| f690b8e505 | |||
| 5964e4c332 | |||
| 6033b200e6 | |||
| 0ee33d34f2 | |||
| 44cb99f663 | |||
| 99170bc25b | |||
| 07640b1961 | |||
| e5cb41601e | |||
| f9d8e64a31 | |||
| 7bfdb82225 | |||
| 04aefeae55 | |||
| d6cad30c9f | |||
| 28358306e1 | |||
| 5539a982b5 | |||
| ed22433178 | |||
| 127876bd1b | |||
| c5cae05683 | |||
| 2f3cbdd7c5 | |||
| 9c682e2cc0 | |||
| f31dfd49d9 | |||
| 731b356c95 | |||
| 6d48a01801 | |||
| 3ec06fa3cc | |||
| 5a1d4ad491 | |||
| 581d67177d | |||
| ac6347fece | |||
| ebb42aa419 | |||
| 8223f9742e | |||
| dfdacba649 | |||
| eeeffd1ad1 | |||
| 93fc797b2e | |||
| 094d6de8e0 | |||
| 3d9fc4c40f | |||
| 3cdc207b50 | |||
| 5acd895e0d | |||
| 3715af2172 | |||
| be4d7a6249 | |||
| 04db1ff7cb | |||
| 5f1a04cfa0 | |||
| 4783e22fdc | |||
| db4903cdc4 | |||
| 248e37c292 | |||
| 8a8ba43bde | |||
| 5c0bc4913e | |||
| ca84ed7e63 | |||
| a64fb40d7e | |||
| d11102c643 | |||
| 745b5328eb | |||
| 5fefc74da4 | |||
| a3a73426bf | |||
| 276d867ab2 | |||
| 49b6a1725f | |||
| 4c67f4cc26 | |||
| b369cc4afc | |||
| 52a7f3e2dc | |||
| 85d1048762 | |||
| 8b80c79feb | |||
| 9bef4cb797 | |||
| 39edd3bdcd | |||
| b4585ce13a | |||
| e47790c3f8 | |||
| 5098088237 | |||
| b1c0d614a3 | |||
| 66c9d29539 | |||
| 655c8e609d | |||
| 948a0713d5 | |||
| b43d883db6 | |||
| 73367a10ea | |||
| b9eba042c0 | |||
| 17a33166a4 | |||
| ffbaf87dc9 | |||
| 8ea74c38c3 | |||
| 5789f20794 | |||
| 133e60410c | |||
| 57a8622e9a | |||
| 634039d81a | |||
| b9c9990bdb | |||
| 1e31e737f5 | |||
| 5e3f579431 | |||
| e9e4df8d01 | |||
| a3cdc4fb83 | |||
| c034e5922f | |||
| 3d8d726a52 | |||
| 1b09ac4757 | |||
| f7012d8275 | |||
| e61950267b | |||
| 4c9ff08f4f | |||
| 8668488a20 | |||
| 31f8dc5cbf | |||
| a477129bc1 | |||
| c46b8aecbc | |||
| a010b6177c | |||
| d38b7012e9 | |||
| 6a6152eb0a | |||
| 15121e6d4e | |||
| a8e44d0093 | |||
| fd8f74cf43 | |||
| 472e4da100 | |||
| 607fe8ed4b | |||
| a0a479eb53 | |||
| 04cdb15ceb | |||
| 180e0a3645 | |||
| c931800642 | |||
| e839d1a129 | |||
| 1efec52b33 | |||
| 3799481c35 | |||
| c1025612d1 | |||
| e07a8fbe31 | |||
| 2d52cb1d55 | |||
| a1cdbe5762 | |||
| 2f3bbf1f59 | |||
| 7ceb13d041 | |||
| d04d93eb9b | |||
| f6a3bc9168 | |||
| dca79490da | |||
| abf37168de | |||
| 67fadf0c94 | |||
| 1b29805fc9 | |||
| fcd3bbecdf | |||
| 3d767ffcc7 | |||
| f88711ebf9 | |||
| 17cd564ba6 | |||
| 8bb3bba7e8 | |||
| 5d3dda7a9d | |||
| 5761c182ee | |||
| 0e23e23a7c | |||
| e2736f4db1 | |||
| 1ea78293d0 | |||
| df0a195f38 | |||
| b00d23c46c | |||
| ed4966a3ff | |||
| b03359a3db | |||
| d4e07e981a | |||
| 2d097d0fd7 | |||
| 278b4547f4 | |||
| ee84127892 | |||
| 3724c40afd | |||
| eaada4cf1f | |||
| 033b25de32 | |||
| 32a12d1d6f | |||
| fe27b0e2ba | |||
| 66fcd4c5a2 | |||
| ccdeb6dc0a | |||
| b579262869 | |||
| 11f1dd1623 | |||
| 6a9a25fc54 | |||
| ef47f6cfd2 | |||
| cecd75063e | |||
| 6dad482efa | |||
| 4aae4489b9 | |||
| 1b00dede02 | |||
| 5700075b7a | |||
| 5a461f5bb3 | |||
| a6cbdccadc | |||
| b662a577ea | |||
| 128b1a423f | |||
| 5e41599d13 | |||
| 2cab94f56c | |||
| eedd673d4c | |||
| 25ffde5a42 | |||
| a06134cd6c | |||
| fa57fee9bb | |||
| 404bfdcf30 | |||
| 87d81a6d92 | |||
| 7d41dc1616 | |||
| c1e1502d35 | |||
| 73949712f6 | |||
| 61778974a4 | |||
| c817deb652 | |||
| 802a0117a7 | |||
| 9b2a450133 | |||
| fcfbe920d5 | |||
| b57331f450 | |||
| 944a61100b | |||
| f8ae1a9997 | |||
| 8ea015aaab | |||
| e38019d74e | |||
| 7da892b49a | |||
| 60c84abfe4 | |||
| efa1ac3807 | |||
| f6b7ab7994 | |||
| c0b6d159fc | |||
| fa85685baa | |||
| d1883bdd9d | |||
| b4ff4df808 | |||
| 3254bfa619 | |||
| bf26cf175f | |||
| 2c287965fc | |||
| 4551ded37e | |||
| b4d05af28f | |||
| eac742e3db | |||
| 57a1201b11 | |||
| 85a8a71fc5 | |||
| bd6bb510f8 | |||
| 0cf8221e62 | |||
| 36d4e8fa62 | |||
| a384290356 | |||
| 8b9226d820 | |||
| 35e639c599 | |||
| 3a7f5fcf03 | |||
| 881cbd6d2f | |||
| b319b710df | |||
| dd1ad34cea | |||
| 9406893722 | |||
| cea4d89697 | |||
| 7bbd31fa2a | |||
| c6d9a04dba | |||
| 77fc1aecb2 | |||
| 24da541747 | |||
| 02f7e9c800 | |||
| cbbc2f42e8 | |||
| 7e73dad7b0 | |||
| bc67fc338e | |||
| 6054e7be0a | |||
| a7f31c59a8 | |||
| 345e97f296 | |||
| 7716d4592d | |||
| 7e4eba9eed | |||
| a1089c9dd6 | |||
| 9a032e7422 | |||
| 55dddb69f7 | |||
| 580d42a63e | |||
| 1a1416aaa4 | |||
| 1d74450216 | |||
| 446a0a174e | |||
| 6405273059 | |||
| bbd0a41191 | |||
| 7bc9003fb8 | |||
| 7c745cd9c6 | |||
| 202f7f9107 | |||
| 1e1fbb4ccf | |||
| 0b17e5d288 | |||
| 3b046c409b | |||
| 9eeb56fe3e | |||
| 3124f8133a | |||
| f99ca77461 | |||
| 0bc1c08d4f | |||
| 3fcf44ae32 | |||
| 4d1dc14b45 | |||
| e4710f54f8 | |||
| ce983ec34a | |||
| bf9b535f66 | |||
| a2f0110f3d | |||
| 367924e669 | |||
| 9c6998c6bc | |||
| 1bf848e418 | |||
| d62e474850 | |||
| 7546333fbc | |||
| 405bb0ad90 | |||
| b7e9f8ce1e | |||
| 00a2da7d18 | |||
| 86c29e23df | |||
| 8765d96c4b | |||
| ee6233b893 | |||
| cebdb43234 | |||
| f65da3d98f | |||
| e6e13b2609 | |||
| 96774bb98c | |||
| 681301ab9b | |||
| 0d68dfcbc1 | |||
| 0a17e9a236 | |||
| 8597d2c681 | |||
| eca53baf6f | |||
| b7bc4ea43e | |||
| 6ab2f35f74 | |||
| c015153e21 | |||
| 7a74418c39 | |||
| 6033b3599e | |||
| 37d98063f0 | |||
| 7fe85b44f9 | |||
| 68125a7f50 | |||
| 166879a1c8 | |||
| b518569674 | |||
| 6a25e05e3c | |||
| 179862b10a | |||
| 339cf51c0c | |||
| c8d06997f5 | |||
| 57979f3c9b | |||
| e70a0e4e0e | |||
| 6268fe957a | |||
| 49ce5eecdf | |||
| 4e1da2b6d0 | |||
| 3f7d24bccf | |||
| de0c051e51 | |||
| 1ef99e657f | |||
| 7cbe1c0a44 | |||
| 8c45f18409 | |||
| ae3c02abae | |||
| 543d4af682 | |||
| 19c49fa82b | |||
| a723b8de7a | |||
| 21c3926714 | |||
| 3d79bf82f4 | |||
| 6338f88db1 | |||
| fdfe60dd09 | |||
| cdff2d0d41 | |||
| eb290bf362 | |||
| 18618deea3 | |||
| 67b64f5ec6 | |||
| f394f2c57f | |||
| 46670c7eeb | |||
| eefc79b5b2 | |||
| 744dd4893d | |||
| 7eed86de38 | |||
| 1c04f5a03c | |||
| 9d9d74faad | |||
| c2342776e1 | |||
| 09aa89f3f9 | |||
| a8430e09f5 | |||
| 768b039cde | |||
| 3f1b9a6b4a | |||
| 638e29b5d6 | |||
| 5abc48269b | |||
| c81b17c1cb | |||
| c928bc8ce3 | |||
| f33a4975ef | |||
| 5920137968 | |||
| f636e1d45f | |||
| 7e6a243df9 | |||
| ad7ba8f029 | |||
| 3096a26b59 | |||
| ff2a719bf7 | |||
| b6c4aaac8e | |||
| f7ad4ca025 | |||
| 4666b7fb69 | |||
| 125ce48525 | |||
| 114d12b060 | |||
| bf1566884c | |||
| fa7b6a8cec | |||
| 9855104120 | |||
| 36f7125513 | |||
| 6ebd5ddc53 | |||
| 2bc4103ea9 | |||
| 01eab7dccb | |||
| a95dee3d09 | |||
| 891c85a2c1 | |||
| 386f9b2074 | |||
| 325d5a6bcd | |||
| f05d7f4176 | |||
| a8ea2e05a4 | |||
| 8bcb88d6fd | |||
| f9bd431d56 | |||
| 0c6e180937 | |||
| 2c5fcd7d2c | |||
| 1a19c761ae | |||
| 71b8823625 | |||
| 24cab0eabd | |||
| 3e5c236942 | |||
| a3f74b2f68 | |||
| 2d09237bd7 | |||
| d26b94db61 | |||
| 017c41bc5f | |||
| d7857dd026 | |||
| 7ac5a172fa | |||
| f44a64a705 | |||
| 490b630e2d | |||
| 1265dc6334 | |||
| 69f994e1b2 | |||
| 0f841f194f | |||
| db6aecda47 | |||
| 83ca78a5fb | |||
| 2f0ae47c8e | |||
| 11a2d81738 | |||
| bb4ea2db67 | |||
| 70a2321cc1 | |||
| 2331742384 | |||
| d41ea73765 | |||
| 5a5616f258 | |||
| bd3108a40c | |||
| 8c9a12e3a1 | |||
| fe45fa81fc | |||
| 3e30600f70 | |||
| 5c05ec5b98 | |||
| 7024117709 | |||
| fd38b708fb | |||
| 153020283c | |||
| b41d1e0eaf | |||
| fcbfd00b98 | |||
| 51edb08796 | |||
| bb087fdc45 | |||
| a4a2196cc2 | |||
| 8ab96afd5b | |||
| f5b230a2ef | |||
| 076defb0a5 | |||
| 76660ff15b | |||
| 60471ecc1e | |||
| 9e68ba1ea3 | |||
| 414dddbf59 | |||
| a0c61484d2 | |||
| 7f6787c8b8 | |||
| 8c74dcde28 | |||
| 27332a8a17 | |||
| 8733da346b | |||
| e644f5be05 | |||
| 7007601655 | |||
| b1fc1cd11e | |||
| 919edcf1e1 | |||
| d99260c614 | |||
| cf9d1ba541 | |||
| d6628419c9 | |||
| 75eb5491d5 | |||
| c32d8f38ed | |||
| 9f22c35519 | |||
| f775500932 | |||
| fdbdb76c39 | |||
| af26e17fa7 | |||
| 769bf51359 | |||
| c81c316a46 | |||
| e5192ceddd | |||
| 89ddf97fa0 | |||
| 79b2eb0ff7 | |||
| 0ab0c7b8ed | |||
| 1d582a515c | |||
| a4a6064316 | |||
| 910d95a7b6 | |||
| 3cedadd738 | |||
| 3ba2d4428b | |||
| 7a545df299 | |||
| 2b3d6075e2 | |||
| 1073a524b9 | |||
| 6dd9cb6fb8 | |||
| 6789755b06 | |||
| e7dae3a9c1 | |||
| 1f78a15db2 | |||
| 79a4f42ef9 | |||
| 8fdf3799a2 | |||
| 4b095ab61f | |||
| 757d617ef3 | |||
| 17923c0d9e | |||
| c9f9db9069 | |||
| 90d03c784c | |||
| ea436444f2 | |||
| 209bb4290f | |||
| 7d9d2e508d | |||
| 5bd8e7d4e8 | |||
| 30f19be555 | |||
| 3e140ddcfe | |||
| 81143324a5 | |||
| fabb646a0b | |||
| 85131e359e | |||
| 5a0f8019c7 | |||
| f067dc8b32 | |||
| 14110cdea8 | |||
| f885296fb2 | |||
| a6c5f46f2d | |||
| 145ea35ac3 | |||
| 1399ed198c | |||
| c2fb99ce49 | |||
| 53542a9d37 | |||
| e77690c268 | |||
| d8e45ef866 | |||
| ffc24ad518 | |||
| b79c17b43c | |||
| be415dab1d | |||
| d0278dad15 | |||
| 927ff2bca7 | |||
| fc7b9029c9 | |||
| 187b529dbb | |||
| 2061947138 | |||
| 04c24cb686 | |||
| f38935459b | |||
| a6ce907a4f | |||
| f1dbfffd82 | |||
| a46d344db2 | |||
| 5476a03799 | |||
| d4569d66ac | |||
| c4ce6d9ab1 | |||
| fa083142a7 | |||
| 8aa8451434 | |||
| a1f81bbe97 | |||
| e9669c6594 | |||
| 690e7144c1 | |||
| a53b3d496c | |||
| be7f436951 | |||
| 7708753ed3 | |||
| 95d5ab706a | |||
| 7afb4e68f1 | |||
| 4caad33c9a | |||
| 1ece12b907 | |||
| 88c2f8504e | |||
| 9137a9ec80 | |||
| fad35e7e00 | |||
| 009925724e | |||
| 8504f30594 | |||
| 96116659be | |||
| 1e7df3606a | |||
| 603029b106 | |||
| 4ca54b2f1d | |||
| 555e24dc59 | |||
| a651aaea00 | |||
| 258b804941 | |||
| dbb4e67737 | |||
| ec1a5d1318 | |||
| cdab6cfdbe | |||
| 33b3d5edfb | |||
| e1aa6f7e84 | |||
| bfc1665d87 | |||
| 9e937f8113 | |||
| d114d9e386 | |||
| 4e8d1366b6 | |||
| 8afb0255f0 | |||
| dac6c662c5 | |||
| 577229bb6e | |||
| b5fdf4f1da | |||
| bf7d383066 | |||
| 6e977b668c | |||
| fdc52d933a | |||
| 316f7309a1 | |||
| ff75a28080 | |||
| e7b5e816d0 | |||
| db41bc2870 | |||
| 585aeb7f91 | |||
| 1b644a164e | |||
| c4e42f46a9 | |||
| bff4d1239c | |||
| 8a89e160ed | |||
| 198052358e | |||
| 6730b7d656 | |||
| f917efd430 | |||
| 7d51fc7dfb | |||
| b192495bbc | |||
| 34a697fc37 | |||
| 2dda514cb0 | |||
| 82d667e30c | |||
| 29b0b9cfcd | |||
| ed7f92ebc7 | |||
| 93ae8f4066 | |||
| 6b030ac9be | |||
| 8470c44bb0 | |||
| 4c75540ca2 | |||
| 70f43b0003 | |||
| 91452f3b58 | |||
| daeb396fd8 | |||
| ad649d9670 | |||
| 0038722d0f | |||
| 6a00664b2a | |||
| 2956543d27 | |||
| 0fcf722af4 | |||
| ceebdbfdd3 | |||
| 1b05af7f8e | |||
| 123b7b5537 | |||
| 5fa1257e2c | |||
| 5c405a6f40 | |||
| 03b5d6125b | |||
| 2376811df8 | |||
| c48997608d | |||
| 8a5285db41 | |||
| c4d6fc943a | |||
| 3655df08ea | |||
| 7858e6aa7c | |||
| c909988aa5 | |||
| 5deaba9669 | |||
| f162b6662b | |||
| 2d5f342ee8 | |||
| 85a2aee79c | |||
| a4cc671c22 | |||
| 2912138ef2 | |||
| cdae489afa | |||
| 51ad0c18ff | |||
| 998255fb1b | |||
| edfb5811cf | |||
| c733b32672 | |||
| 38862c1a09 | |||
| 03161c81df | |||
| 264c73747a | |||
| 5e53e410ce | |||
| b42ec57f6f | |||
| 4d0f664f50 | |||
| 2ea3b8b068 | |||
| 0810492a2c | |||
| c5178979ce | |||
| 08bdaf07be | |||
| 832764aa3a | |||
| 43c87d0cb6 | |||
| 6a6b944c00 | |||
| 90eb83dcb2 | |||
| 31b722f9d3 | |||
| e3351a9686 | |||
| 19dea9095e | |||
| 3b023da602 | |||
| 33d0a72fce | |||
| 5136fc535f | |||
| 16a21ade8f | |||
| ec2b2dc291 | |||
| 2703a5aebd | |||
| 4bdd21b281 | |||
| 499d41206e | |||
| ece008e790 | |||
| d1cfa88bc0 | |||
| 0e28b55184 | |||
| afccbbe04d | |||
| a4cec09781 | |||
| 54d70ec738 | |||
| 39c3a38e2d | |||
| 699897ddf8 | |||
| 00d3af4816 | |||
| ceadbbcdc1 | |||
| e703b53b3a | |||
| f037cc3c74 | |||
| 7dec942ef7 | |||
| 1d0ca22572 | |||
| a6decc3cc7 | |||
| b58cc107e6 | |||
| a593306a06 | |||
| 0177590699 | |||
| 787fe28fd0 | |||
| 541568e09f | |||
| 5dd7ef6895 | |||
| 153df79699 | |||
| 4ad9af71f5 | |||
| d76671a601 | |||
| ea094a4fbc | |||
| cb11dad4de | |||
| 817cf192c8 | |||
| aec00ab96f | |||
| 48b288cdc2 | |||
| 66d257f620 | |||
| dd14218c34 | |||
| 918231795b | |||
| 9f71172437 | |||
| 69e3b23960 | |||
| 7b426e8103 | |||
| 27e1043fc4 | |||
| 76518712c4 | |||
| 64843d6b0a | |||
| 87fa661591 | |||
| 2ec6d22131 | |||
| f6ac92dc39 | |||
| be69f7ad20 | |||
| 9a7cc494f7 | |||
| 8c3d4d4eda | |||
| 636ca7a74b | |||
| c57c1e94d1 | |||
| 1e1c2cbb7d | |||
| 5399b6f438 | |||
| 41b6c8434a | |||
| 3031b9e6a5 | |||
| 0d5331df59 | |||
| bca75d4930 | |||
| a70965eb6d | |||
| ad21bfda4d | |||
| fef46ffacf | |||
| f803745fc6 | |||
| c36ab1c6ed | |||
| 28f9d7f28f | |||
| 5b225186c4 | |||
| 09bb2a6054 | |||
| e1ea1237b7 | |||
| fb65f5d1ae | |||
| ff439ecd7f | |||
| 4cc4df87ff | |||
| 56aabe20bc | |||
| 4b624381e0 | |||
| c5508f34b3 | |||
| 3c667d7652 | |||
| 4664427784 | |||
| c747ffef95 | |||
| b5b1777304 | |||
| 228004b472 | |||
| a1f91c08ad | |||
| 96dc27d9e6 | |||
| 11f9f15199 | |||
| 991a92429e | |||
| 4f17dc0716 | |||
| 37c7fa9fb4 | |||
| fd2f700390 | |||
| 97255b2b7f | |||
| 8d32141b8f | |||
| 7700779e2c | |||
| 1cc9d2ce5a | |||
| b9980df1b7 | |||
| af8b256a00 | |||
| 324c39931c | |||
| 113325673f | |||
| 857b02706e | |||
| 1e0f1b39b0 | |||
| 6a429facb6 | |||
| 8663a6c7a0 | |||
| b6ec124ceb | |||
| 85a3b72c71 | |||
| d9b489d471 | |||
| 3289c6c305 | |||
| ec83adde3f | |||
| 206c8e1189 | |||
| 07545ba49c | |||
| 8312497d6c | |||
| eed2bed0bd | |||
| 49045cf08c | |||
| 55f7a99d1b | |||
| 98addf78b4 | |||
| 4b42ec3218 | |||
| e469d9d1f9 | |||
| d53d9d88fb | |||
| ed484deec6 | |||
| df9502dd06 | |||
| 6001254160 | |||
| 2b0c4ba215 | |||
| 1e9088f7d2 | |||
| ec9e283d34 | |||
| 20e7d431a6 | |||
| 6c745e6e9b | |||
| d1635e17f6 | |||
| f9e5881b45 | |||
| 869505a659 | |||
| 895d265603 | |||
| e5e60c6bed | |||
| 568e6c8093 | |||
| cf01f1a264 | |||
| be7de02a1d | |||
| b8d88f0dc5 | |||
| 698bc0cdac | |||
| 48a7a863ae | |||
| f2c4fa2b46 | |||
| f996d6cc7a | |||
| 0dbf3ef30a | |||
| 76b558c831 | |||
| c10bb66b12 | |||
| 89a391d001 | |||
| 0fef63956f | |||
| 63c2af4fa4 | |||
| 3ff7ddbea7 | |||
| 21733e7bda | |||
| 3fa60aaed8 | |||
| 9422e37c45 | |||
| c074c8c9d4 | |||
| 27c4956046 | |||
| ebbd7c48d3 | |||
| 079edee146 | |||
| 87f9452bbe | |||
| 9c0c64e87b | |||
| 3b65c120e8 | |||
| 3289835b53 | |||
| 7745d37d6c | |||
| 37e440b093 | |||
| 7792364e8c | |||
| 3a4ad03492 | |||
| cac7dbc607 | |||
| 7326c9cb73 | |||
| beb8e76bb4 | |||
| 43e37caf46 | |||
| a985798a86 | |||
| d09cca948f | |||
| 3f52031b86 | |||
| 7448116f37 | |||
| 4e0d54fe21 | |||
| 10d8275962 | |||
| 157cbc2f68 | |||
| 7d64d2e92a | |||
| f86261c812 | |||
| e1eb33a320 | |||
| cadcc20ea6 | |||
| 56ad7642c9 | |||
| d1695d3408 | |||
| b51fbe5c50 | |||
| 04c0ba65d2 | |||
| c2f5e7d88c | |||
| f456a92f08 | |||
| 48419d8fd9 | |||
| 0ef5dbac4a | |||
| 8014e0d993 | |||
| 739238659e | |||
| 6de593c771 | |||
| 4e9fd3ee9d | |||
| 900056be44 | |||
| 221cb6d5b2 | |||
| 83e693c78a | |||
| 8db5848e91 | |||
| 27807f02c5 | |||
| 30e31ff164 | |||
| ee0fe85cd0 | |||
| b2270b2f4f | |||
| 141252b73f | |||
| 7c4ac2e3a4 | |||
| 46b731b2d3 | |||
| bd76e47976 | |||
| bd2c878b35 | |||
| 84ee9591d1 | |||
| 15b27fa35f | |||
| e41536484b | |||
| d3004fc833 | |||
| 395c5fc922 | |||
| c20de314cc | |||
| 4881e6477f | |||
| 93609837dd | |||
| a8b3c20e66 | |||
| db2fba1faa | |||
| bf9cbb4491 | |||
| 18f32cfbd6 | |||
| a5a3ef17b8 | |||
| 326b827cc6 | |||
| 961efac8e2 | |||
| 418d26d250 | |||
| 07fb2a3e93 | |||
| 5006cca67b | |||
| 735e3b71d6 | |||
| d9da8228d4 | |||
| b5a7e66134 | |||
| 00bc96a2b7 | |||
| 5dd66771fb | |||
| 6f19a7deac | |||
| 811585b00f | |||
| f610377363 | |||
| 8c549a28df | |||
| cab22ffeac | |||
| 60c9f576f6 | |||
| b619c2069f | |||
| 3efdcb95cd | |||
| 4b11862ecc | |||
| c5e9b187ae | |||
| 04fc574229 | |||
| f6b6665fed | |||
| 0da9086397 | |||
| c5852c1237 | |||
| 5bbd7cb013 | |||
| a3b4a09fd2 | |||
| bf783c42bd | |||
| 57e832dc65 | |||
| ab2d301968 | |||
| f1f961a43e | |||
| 8e345bd09c | |||
| 0996c307ac | |||
| fb8e505590 | |||
| ec2804a78a | |||
| 29d2ee3273 | |||
| 6e3e6a1944 | |||
| 1005fd2872 | |||
| ab666cc267 | |||
| e79df21353 | |||
| ff0ddbcac0 | |||
| d1604137cc | |||
| 969efff611 | |||
| 293f7cca28 | |||
| 45a48cfcb2 | |||
| 822cc6a781 | |||
| 658f02a6b9 | |||
| 8d675d9bc0 | |||
| fdf135d877 | |||
| a2bb144556 | |||
| c867ed9de4 | |||
| 39ef596bdc | |||
| a9bcaa1493 | |||
| f05163fbeb | |||
| 7ad3e0c544 | |||
| 24882a9324 | |||
| 9bb6ead2b1 | |||
| d6bcbc5cdf | |||
| dc4b261c68 | |||
| 9262f13eef | |||
| 82998d0cc0 | |||
| 8949cfe584 | |||
| c10d1fe279 | |||
| 9ed5ee8310 | |||
| 7b266ef475 | |||
| 09f1a4fbb8 | |||
| c7f55381a5 | |||
| 81f35ffe73 | |||
| 597b362603 | |||
| 0163e4b4e4 | |||
| 98a77ee649 | |||
| c9edd2c8de | |||
| 379ad3a8e7 | |||
| aca64b5dda | |||
| d34639b79b | |||
| 0d4b157207 | |||
| 58160d9624 | |||
| 57e34dfb3c | |||
| 67ab19289d | |||
| 7718bf6858 | |||
| ee467d0203 | |||
| 9b7265810b | |||
| 24dc1092b1 | |||
| ab953f1381 | |||
| 83174e996e | |||
| 38069048fd | |||
| 9fda651595 | |||
| cb13ae57bc | |||
| 6a45d30715 | |||
| a43f24617d | |||
| f11a148d2d | |||
| 73cf4c0e49 | |||
| 8ea3cd1cb5 | |||
| 62979ae06d | |||
| 5e998288d6 | |||
| e01b851e1c | |||
| 2e6a40be96 | |||
| 02c1ec5efc | |||
| 78a6ba31d8 | |||
| 1ae23df0ba | |||
| 36ba9a7309 | |||
| 7ee129b21a | |||
| b3d4123007 | |||
| 5bfd330692 | |||
| 49125d1a7d | |||
| 6050ef41a9 | |||
| 0d809688ee | |||
| da20e80de6 | |||
| 1f46bf2e28 | |||
| f345dbe7e4 | |||
| 490c8d856e | |||
| 897a5b8de6 | |||
| 155cb4f158 | |||
| 445cf5860a | |||
| 6cb1bae032 | |||
| c0c9d667cd | |||
| b946032f56 | |||
| df8826240f | |||
| 5dda4f95a6 | |||
| f3822e6ffa | |||
| 084ad26b73 | |||
| fafecb491e | |||
| 0e6a590305 | |||
| 0e8c9fda5e | |||
| d2ac283570 | |||
| 69bdc16751 | |||
| b8325a38a1 | |||
| 403a1a7e1f | |||
| cf481d2fc4 | |||
| c3e3fc4dba | |||
| 254ad1b26b | |||
| 2c642afd04 | |||
| e43fab7c01 | |||
| 55f2914163 | |||
| 96d7ac7c6a | |||
| c9be096e88 | |||
| e18b8642c4 | |||
| dad83c9004 | |||
| f0a86c360c | |||
| c6444b7d05 | |||
| 6b81b6931f | |||
| b391fb3faf | |||
| 2649c38bf7 | |||
| 81266f2f71 | |||
| 05189f9ff5 | |||
| a3f8783dca | |||
| c20d23a552 | |||
| d2ed926077 | |||
| c0b3ad8253 | |||
| 30495f780a | |||
| a2114c44ad | |||
| 0f06a927ff | |||
| c5c41b8ab5 | |||
| 1f4ca54938 | |||
| 720e3e9272 | |||
| a42400272d | |||
| 19558b48c1 | |||
| 744d875838 | |||
| 59d2523c49 | |||
| 13fdde5e5d | |||
| 86e73c1bae | |||
| 6cd558fd5c | |||
| 8f45c7d27e | |||
| 3518b18351 | |||
| 6dea106650 | |||
| 123a29d126 | |||
| 9a8383de24 | |||
| 2453201d7b | |||
| 14deef3adf | |||
| 8c191e5ba8 | |||
| 20fc4a2efe | |||
| 49a577dd59 | |||
| 8b8f5d45c9 | |||
| cec687bad2 | |||
| 9891e2d986 | |||
| 24b23e2383 | |||
| 413aa106a2 | |||
| 097dcd724f | |||
| 1454880fb7 | |||
| c6a712dec3 | |||
| 2c888b86f5 | |||
| 7481dcf520 | |||
| 435a34cd60 | |||
| 23ffcf8f89 | |||
| 89d6a09401 | |||
| b8fce6a8e1 | |||
| 523e7eb397 | |||
| 30a3668779 | |||
| d95ea479c4 | |||
| baf18a6379 | |||
| 92831ee7d4 | |||
| de823996b5 | |||
| 936974f12c | |||
| 0c88507dcf | |||
| 9184095b34 | |||
| f1eea58b6b | |||
| 49f2206ef0 | |||
| 8f94a14063 | |||
| 9843e0c436 | |||
| e3149cd2b0 | |||
| 7c5d2ab029 | |||
| 6422be116b | |||
| 50eef193ff | |||
| 8092d98ea3 | |||
| ed42891509 | |||
| 6124226428 | |||
| 6c22590d4a | |||
| 950d9ae23a | |||
| a12ff75fee | |||
| 681413bb97 | |||
| 1ebe2c8728 | |||
| 41dd21ac7b | |||
| 6b6d9706bd | |||
| 1b57a79636 | |||
| a1f3b7d53f | |||
| 1118ef8f19 | |||
| 4b9526adb1 | |||
| ef3f42dfab | |||
| 6f018668c2 | |||
| f20d6e8d05 | |||
| 0574bf8aba | |||
| 7ea9f8ddcc | |||
| e4d9cb77fc | |||
| 7eee33a6a0 | |||
| 3bbafe14f5 | |||
| 1dc048662b | |||
| 7546651a30 | |||
| 6d16131b03 | |||
| 7eb196b4d1 | |||
| 1e8f7f7ba8 | |||
| eefbd1610e | |||
| 0f8af51da6 | |||
| c14dad91b2 | |||
| 2538262531 | |||
| 453a135a3e | |||
| 0cfa6d8097 | |||
| 7e77f3b902 | |||
| 71d2467963 | |||
| a6140c799d | |||
| bd7217b090 | |||
| cc45f4f212 | |||
| d95710f8e0 | |||
| b3703f1f91 | |||
| 104d63fd7c | |||
| df994764a5 | |||
| 764ef2dcc9 | |||
| 1dddc22a9b | |||
| ebac5c4a70 | |||
| 59a3715e40 | |||
| 86bb2f325e | |||
| eb370afe4a | |||
| fc06b1762f | |||
| a1fed86b28 | |||
| a96a265a7b | |||
| a2d65bb523 | |||
| 7c966fa775 | |||
| 8042d6f892 | |||
| d4c8c0bb52 | |||
| 148d567aa4 | |||
| 85e96c92ed | |||
| b25e440ba9 | |||
| c4d874cdb1 | |||
| 4e96cc4bbb | |||
| 78f928544f | |||
| 0edbe77903 | |||
| ccf8c3bd83 | |||
| f8020f4728 | |||
| 23bded5697 | |||
| 9df7292af6 | |||
| bcb05fbc14 | |||
| 7adc495692 | |||
| 69f8ee8ee2 | |||
| 4acb97e02e | |||
| 7cd840bf83 | |||
| 9577736788 | |||
| f7ec499ee9 | |||
| a64e1b3397 | |||
| abeba63ed5 | |||
| 0dd05887d2 | |||
| f8b3873b83 | |||
| 63362c97f5 | |||
| b07b214293 | |||
| e368ae3230 | |||
| 9cf627a656 | |||
| 5d383f1885 | |||
| f9b7ede7ff | |||
| a2a44bb7ef | |||
| 1f37ca437b | |||
| ac77e047f4 | |||
| 02a6baad8a | |||
| 799ef3bc0d | |||
| b337dccd0c | |||
| 8867bf262b | |||
| 0543670734 | |||
| 9c66afb6fe | |||
| 0dc8241605 | |||
| 7ca5c4099e | |||
| e40c8fa164 | |||
| df88d0cbea | |||
| f73e785662 | |||
| 1bb685cc57 | |||
| 5d4da5ede9 | |||
| fe295788e7 | |||
| f8b3eaedb5 | |||
| e5e83eba05 | |||
| 28a35c9883 | |||
| d8a6c7c1bf | |||
| c8f6806287 | |||
| 8bf57814a4 | |||
| c2d4338024 | |||
| 6410862b15 | |||
| 4845668a4a | |||
| 146adbf831 | |||
| d1f291acb8 | |||
| 4b7c3010e0 | |||
| 4039f66530 | |||
| 4ce6362f4c | |||
| 5765ebef08 | |||
| a3eb501225 | |||
| 82706bf152 | |||
| 5e852ba1fd | |||
| d49cdb2ded | |||
| a8ad1f8097 | |||
| 2ea3c4acac | |||
| d1e3bad24f | |||
| 06f46e4b14 | |||
| 54714fd989 | |||
| 912d308b9d | |||
| 0b71e4a98a | |||
| 900bef9ccf | |||
| 7106d1e605 | |||
| 6840ec0d09 | |||
| 0e8e811b14 | |||
| 3dc986bbf6 | |||
| 34813dc07c | |||
| f79d75076c | |||
| 57b5ce84a5 | |||
| 395338f46a | |||
| c33cd27626 | |||
| 2ca4512443 | |||
| 76b4dc7346 | |||
| 53609d2ecd | |||
| d24054ac00 | |||
| 2c0619f69d | |||
| f039aeb4c8 | |||
| ab0e47d402 | |||
| 03128b7530 | |||
| ed3e79325f | |||
| c4c715fcd0 | |||
| 86b83ddbc9 | |||
| 62c43eaf53 | |||
| 33cf2243a8 | |||
| 02a1c2f096 | |||
| b841150c23 | |||
| 3db19ba2eb | |||
| 22d1f50455 | |||
| baee140518 | |||
| deaa4529ae | |||
| bed29749f2 | |||
| e57cbd3348 | |||
| 2f788445a9 | |||
| d4e55c69c1 | |||
| d07351c8ba | |||
| 2412caf494 | |||
| 9b6f75afd3 | |||
| a4b2a6537c | |||
| cfaa7d9e54 | |||
| 0ca3012c67 | |||
| 42ebece808 | |||
| 0d7510c129 | |||
| ebe697f62a | |||
| 29240fd14b | |||
| 97f4d18a13 | |||
| 8ef0dd69da | |||
| 30d41b33d1 | |||
| 2ce06cfe08 | |||
| 3b1a1125d2 | |||
| 93cdfb566b | |||
| b61ac2069d | |||
| 6e8a71ec34 | |||
| 3b3fc0151e | |||
| f5643f2332 | |||
| 38e5d961f6 | |||
| 5be15bed82 | |||
| deb6aa8cc5 | |||
| 3b3e19ee8a | |||
| a713e47713 | |||
| abbc83bd51 | |||
| 7d4aaa8cdb | |||
| 06b3172333 | |||
| 2062cf8fe4 | |||
| 3e15da5bcc | |||
| 520eace689 | |||
| 9ed3c5dc46 | |||
| 7db3834f0b | |||
| 5979b1a1b6 | |||
| d283a8c878 | |||
| 3f2a258930 | |||
| 550d36dc33 | |||
| 46b9d9e359 | |||
| 3e084a3b0e | |||
| 391a663360 | |||
| 068ae2f344 | |||
| 64dfea78b8 | |||
| 3bbcd30396 | |||
| 09bfd3311c | |||
| fdd0e02c35 | |||
| 10b5322b87 | |||
| f01feef636 | |||
| 1aed2b3114 | |||
| adc9b70d72 | |||
| 5f86a70891 | |||
| ea9a84b748 | |||
| c73ca9a6da | |||
| e467e58857 | |||
| bb73777a4a | |||
| a63a62f827 | |||
| 1c6574e69d | |||
| 313785b421 | |||
| b6261f3d80 | |||
| 3dc4de85b5 | |||
| bb0711eb3f | |||
| 0cb647ee1e | |||
| 5a0c37e2b9 | |||
| 4a3ddd5b2c | |||
| e6095e6fd8 | |||
| 33d1d0f8e4 | |||
| d02025b0ed | |||
| 28af99d95d | |||
| 619c8e689a | |||
| c03355d67f | |||
| 417572f3c5 | |||
| c5ed76f104 | |||
| 5721a279c8 | |||
| 0cf8553b2e | |||
| 6d5b865741 | |||
| 7b08e81991 | |||
| 6d4e0ff665 | |||
| bd6ea96ed6 | |||
| 9feccc625b | |||
| 65949662f6 | |||
| 22defb9ad4 | |||
| 37cb4d00df | |||
| a48c0ece4b | |||
| b8c77dc417 | |||
| ed42ae5a79 | |||
| 20f83297ce | |||
| 896dc0fcf6 | |||
| f8a238e670 | |||
| 3b8f2a551b | |||
| 2263633df8 | |||
| 59b7a8ff8f | |||
| 9e6b63e3da | |||
| 5eb63c42f1 | |||
| 1bba1c5ee0 | |||
| f7047d3cfa | |||
| cb16951519 | |||
| 14dc5709ee | |||
| 7d03b0af33 | |||
| 33f7d83c71 | |||
| e469a6b3d8 | |||
| 2971ea6564 | |||
| 02ced9a339 | |||
| 1960a8768e | |||
| f4c8abb1b4 | |||
| ab9630b825 | |||
| e79d6ae971 | |||
| 0348e13258 | |||
| 40c7c7dae8 | |||
| 396695c866 | |||
| a07ae76bcc | |||
| 0221700226 | |||
| d8f3424554 | |||
| 623234c7c2 | |||
| 49aff4eba3 | |||
| 9281d15c70 | |||
| e482826ac3 | |||
| 49e2e292dd | |||
| d721f176f8 | |||
| 58532cd8c5 | |||
| 37fe3ca01a | |||
| 1a01535313 | |||
| 2ba0f5d807 | |||
| 48fa8fd20b | |||
| eeb179fc9a | |||
| 76d1abb9ca | |||
| 68d2aed8af | |||
| dfdecb7e0b | |||
| 2b54414e60 | |||
| 2f03f95e9d | |||
| a828d714c6 | |||
| e36a3dd460 | |||
| b60fda3fec | |||
| 590b237338 | |||
| 237592fbda | |||
| 9a6a301c4f | |||
| 064c057200 | |||
| d1077e6171 | |||
| b90e483117 | |||
| ad25f4c43a | |||
| de22ae4a99 | |||
| d166463bca | |||
| ef326260c2 | |||
| a969e87f13 | |||
| 532fb73c43 | |||
| 0c79aa30ab | |||
| cd68572df5 | |||
| a37cc75bd7 | |||
| 3b8118ac0b | |||
| bdf725f86b | |||
| c8866b7d4c | |||
| 552b211124 | |||
| 680fe999b2 | |||
| 1e380374eb | |||
| 25705ad61b | |||
| 6351d5a283 | |||
| 1666e12db2 | |||
| 728cbcdff4 | |||
| bd047624e5 | |||
| 0e8f5b8147 | |||
| 45d4353cf4 | |||
| c7455ca776 | |||
| 5def0c2eea | |||
| 9e4090f601 | |||
| d1a8ecca70 | |||
| 69415616f8 | |||
| 7eec35e4ea | |||
| f76bf37229 | |||
| 5c335f3acc | |||
| 98f91f8b0a | |||
| 93e568b254 | |||
| e86579b576 | |||
| 672ffefb5f | |||
| c500a6704e | |||
| 3e0c355d7b | |||
| 87da1b2d0c | |||
| 0d45084914 | |||
| 9097433898 | |||
| 0b704e356f | |||
| d847d5b942 | |||
| 694c900cd4 | |||
| 9204086f41 | |||
| d8395dfeb6 | |||
| 39f84d7fe4 | |||
| bbc8c1121f | |||
| 383ffdfe0a | |||
| 8c89c1daf5 | |||
| 706337b8ea | |||
| 9f67477858 | |||
| b703d81561 | |||
| 9e8be6cd28 | |||
| 250960f771 | |||
| bbfb0d669c | |||
| ad14802227 | |||
| d5b1087fb8 | |||
| 150b7cd4e2 | |||
| 20fc8ee3cc | |||
| 47877ec70c | |||
| 91e8aca0a9 | |||
| f809cc2db7 | |||
| b07caa93c9 | |||
| a65c42b7d8 | |||
| 583006b599 | |||
| 29b5837a91 | |||
| 26eadad611 | |||
| 0aafa5a591 | |||
| 0a42863bd1 | |||
| a1f23f3a4c | |||
| badef10f41 | |||
| f1284bd73c | |||
| d2926d2edf | |||
| da45f02f6a | |||
| 67a3a7521f | |||
| 699f87c617 | |||
| a568ed2bd8 | |||
| 6b4366a0a4 | |||
| 5aa957bcff | |||
| f22b5abfac | |||
| 8811f5053d | |||
| b16571bb86 | |||
| d12cf29322 | |||
| 7c8c7984a1 | |||
| a35ddcd0e1 | |||
| ddc95bc7f7 | |||
| ce99a71a52 | |||
| 9882eef93a | |||
| 140667308d | |||
| f50859fd09 | |||
| 86267bd8d6 | |||
| e5f6738280 | |||
| d6acc8debe | |||
| 84c562de1c | |||
| ddd6bb632a | |||
| 85b69b8689 | |||
| b2837d38b8 | |||
| 06582b4cf9 | |||
| f5d0863b2e | |||
| f05c0cd6ce | |||
| 223642904f | |||
| 8317e918d9 | |||
| d497b62fb3 | |||
| 014ba087a8 | |||
| dfa1f480b5 | |||
| 9ce8fcd268 | |||
| 6bd07d3d63 | |||
| 125d820dac | |||
| f77f7e645b | |||
| 82bee848a1 | |||
| 41aa30c93f | |||
| 594c4102f6 | |||
| cf1ae599f1 | |||
| 9fbc499361 | |||
| 3d747571d6 | |||
| 036e02e052 | |||
| d03a9188ae | |||
| a07e6e4013 | |||
| ea0562c124 | |||
| 215b6cc634 | |||
| f7460dfa9c | |||
| 92a285378a | |||
| d033f2235f | |||
| 29016a1fe4 | |||
| d75596c9b8 | |||
| 8ef3f8ade3 | |||
| cdce807375 | |||
| 4c9cf84dff | |||
| f878e38d12 | |||
| 24c572b806 | |||
| abb05eae40 | |||
| 934271b272 | |||
| a3366e6c95 | |||
| 8971bf7546 | |||
| 3a673cc06e | |||
| 8bf09f65cf | |||
| b7f5699b3e | |||
| 7d01550649 | |||
| 05aedc7c57 | |||
| 9af5367aee | |||
| 3c1015942d | |||
| 98329144a0 | |||
| dc990a5309 | |||
| c28dc2bb13 | |||
| c3fa406d60 | |||
| 3c84312ac5 | |||
| a88656ea5a | |||
| 86ad0d2c62 | |||
| 5512567d74 | |||
| d14d7f67b3 | |||
| f6b8a150bb | |||
| ba8ab188d8 | |||
| b188f76b65 | |||
| 48a745e934 | |||
| e1c9cbedd7 | |||
| c53c5b47ea | |||
| 487ac04a1b | |||
| c98771f683 | |||
| 9f990349ff | |||
| 3aed6de76e | |||
| 1be7a45f18 | |||
| 531228f3c0 | |||
| 15a53269d2 | |||
| 7187639286 | |||
| fd72c74184 | |||
| 9912fce370 | |||
| 063850bc20 | |||
| fb8c1ad75b | |||
| 805c6b9711 | |||
| e3fbd46a79 | |||
| 9fc9ca3265 | |||
| 40aee428b5 | |||
| b7fe42a545 | |||
| 5cbd311e80 | |||
| 4f74bf88f1 | |||
| 4df999cbe2 | |||
| 039fe4bddf | |||
| 833bbc5b88 | |||
| c0dfc5bb89 | |||
| b1866205ed | |||
| 50067840c7 | |||
| aa6c9e7f21 | |||
| cf4991979d | |||
| 09cfdfd756 | |||
| 72970d8c7d | |||
| c3d320aa8c | |||
| 3fcbc847a9 | |||
| 151fc3065b | |||
| 7eeb8a2c0c | |||
| 93437e3539 | |||
| ae81396710 | |||
| 7a61b1f4b9 | |||
| 6fd2e6ced0 | |||
| f3ddaf5dce | |||
| 961b408f10 | |||
| 18c5a9aef4 | |||
| 880844d178 | |||
| f55cf6f90d | |||
| ea90b73781 | |||
| e81576b74e | |||
| 4de3380ca5 | |||
| 7f2ecfe721 | |||
| 62c00ba871 | |||
| d55c34f662 | |||
| 76821902c8 | |||
| 504fd3e282 | |||
| 86da74e0bc | |||
| 484bbcbfba | |||
| 500e3b9d85 | |||
| b299c85f98 | |||
| 6a39f5bd1c | |||
| 2cecd66a50 | |||
| ca15df807c | |||
| ce4d8d902d | |||
| 0f83413a8f | |||
| 12a5a2da41 | |||
| 15f53f6d97 | |||
| 40d78f97e6 | |||
| cbe9446e50 | |||
| 890e7beb26 | |||
| 72926fd4ee | |||
| 0d62f4706a | |||
| da20193852 | |||
| cdc07b5d4e | |||
| df0d018b40 | |||
| d02e5ecdc5 | |||
| 7ee96336d7 | |||
| 89ce14bfee | |||
| b5b6e9997d | |||
| 9ea12cf091 | |||
| 5d6ca51ea8 | |||
| 2d0a80bcbb | |||
| a2eea79ea8 | |||
| fe52062f04 | |||
| 6c4f5c78d0 | |||
| d7a65ae09f | |||
| e97a630235 | |||
| f66f15ce90 | |||
| 7b6b9a6e22 | |||
| 4d471306f4 | |||
| e3dbc609d1 | |||
| 67bb18921b | |||
| 83c75f9291 | |||
| fce606ed94 | |||
| 24a9663db4 | |||
| b7dba5f72b | |||
| 60d245e968 | |||
| 689324ad24 | |||
| 5a2d655c4a | |||
| a6b258c953 | |||
| 765df72a6e | |||
| 2b5b1b2a40 | |||
| 55b3776f42 | |||
| f36217fb92 | |||
| 6881dae3bc | |||
| 02c7630bbb | |||
| d80207d69e | |||
| 09a999c8a1 | |||
| e3dd66ddb4 | |||
| f36ffece70 | |||
| d6d74ef77f | |||
| 279c944628 | |||
| ff624c35e3 | |||
| a6deca4da8 | |||
| 14d5685528 | |||
| cb13201fd5 | |||
| e56b50474a | |||
| 83b94092b6 | |||
| fe8318d336 | |||
| 898c009f50 | |||
| b8bdc638bf | |||
| 3537b47ba8 | |||
| 9bde5a6482 | |||
| f8b7583de5 | |||
| d484fbd93c | |||
| 214e5cc6e3 | |||
| a6c123d4e4 | |||
| 363a438fc6 | |||
| 2c4975c5b5 | |||
| f8d96846d5 | |||
| a57fd395fe | |||
| 9feea24516 | |||
| 41f2b5ec8d | |||
| a8e9046c6c | |||
| 67b1db7123 | |||
| 2a59f5ce00 | |||
| 14cf2d9532 | |||
| c10d526385 | |||
| 32efce9f86 | |||
| 533d885923 | |||
| 9903dd5ad9 | |||
| ad5a85ed51 | |||
| f25e5a1012 | |||
| acb7148ac4 | |||
| 585c87a8c6 | |||
| 9d651f85f1 | |||
| ddef0a729d | |||
| 7a0b449ac3 | |||
| f8bcc9966d | |||
| f300ddcada | |||
| c28013930e | |||
| 45b820abe2 | |||
| 034177f0c0 | |||
| 5edbac54d3 | |||
| 09bf5ca5a0 | |||
| 814f9ebc5b | |||
| 6680975105 | |||
| dfdbcd8325 | |||
| 2820371a00 | |||
| 5f1d1869e3 | |||
| a60a4405c8 | |||
| 24d596ee53 | |||
| 0c37fcebe8 | |||
| 1ca7408701 | |||
| eceaf3bfb4 | |||
| 68f8d44e0d | |||
| ab5f27c5af | |||
| f7d074d64c | |||
| 6ad9366be0 | |||
| 32cea2e656 | |||
| 17cf16dd19 | |||
| f533541b67 | |||
| f4ccffc754 | |||
| 6a51e74319 | |||
| eb99d460a3 | |||
| 25a92406ce | |||
| e2f76ff8e3 | |||
| a7cfb276a6 | |||
| a26ea8134c | |||
| 892cf8be29 | |||
| 4880bc49e1 | |||
| 068eb252c5 | |||
| e784171e11 | |||
| 98db29d939 | |||
| 6fddeb89bc | |||
| e51810cb65 | |||
| 27ae0785c1 | |||
| 1dabea71da | |||
| e6fe79daca | |||
| dd54429ff6 | |||
| 47c9866131 | |||
| c39e8b142e | |||
| 1b54306b2f | |||
| 1a193d24ef | |||
| d27bdd02ef | |||
| 9474623ff0 | |||
| caf2439116 | |||
| 4ad507b4e9 | |||
| 9e61e71cb1 | |||
| 1176351701 | |||
| ed835a4973 | |||
| 641c7a6d57 | |||
| 8bfbdc8cf5 | |||
| eae27b0baa | |||
| 6281f981fa | |||
| ee08bfb6aa | |||
| 6a07109e10 | |||
| 28d18a4f70 | |||
| 14e81fca0e | |||
| 6d0cb74b64 | |||
| 29b40bc75f | |||
| d78bf5a93c | |||
| f37b21bf6c | |||
| 7309784487 | |||
| 55426fb047 | |||
| a1172b6211 | |||
| 271a865d01 | |||
| 5050dd13f4 | |||
| 5ce81bf386 | |||
| 40fd3e9ae2 | |||
| d8bffe12d7 | |||
| 7566aa2824 | |||
| c97ede5c34 | |||
| a2416f0b3f | |||
| 89eefbdc6a | |||
| 44f64a852b | |||
| 11ecb09eab | |||
| e6f686533f | |||
| c8dd7d95de | |||
| 81a7af5704 | |||
| e0dd754995 | |||
| 178e024721 | |||
| 99877adb39 | |||
| 5d8779fac2 | |||
| 34e07ef245 | |||
| c2f006f37a | |||
| f19506c3ce | |||
| 313a40875a | |||
| c49acaddf4 | |||
| e2487270e5 | |||
| 2a29b56b12 | |||
| 746c533c3c | |||
| 3ddd2445e5 | |||
| a682132503 | |||
| 00bee0fa54 | |||
| 4d25d2ffcc | |||
| 1db4a96c2c | |||
| 8fef15cd56 | |||
| 64396d15c8 | |||
| 5d209f798a | |||
| 811006ed8f | |||
| 555e9ed5fe | |||
| 49eb11e135 | |||
| a40cc0d9fc | |||
| 23078fe9c4 | |||
| 254c6dc993 | |||
| 66d0b809da | |||
| d0223dc1a0 | |||
| 876b58cebc | |||
| a21c2b4144 | |||
| b38bde7e12 | |||
| 5cce143273 | |||
| 75b97c9e0d | |||
| 91d3e9d924 | |||
| b6819aa8dd | |||
| 60cb12c062 | |||
| 8ea57a4b36 | |||
| dd96425a72 | |||
| 1bdcac1aac | |||
| 70443d06a2 | |||
| e23512d815 | |||
| 8db20a6589 | |||
| e7787b18d3 | |||
| 7f7bbbcfb4 | |||
| 6a966bdb61 | |||
| 702ac16d12 | |||
| 8c9150c223 | |||
| af4e13e528 | |||
| 8951032d07 | |||
| a5a64daf75 | |||
| b3e7510d31 | |||
| 4bc6b69a96 | |||
| 00b0d03dee | |||
| 1c24f3b14a | |||
| 65383f28a2 | |||
| ef1c63169e | |||
| 7cf1f011ef | |||
| 4aeeaed4c7 | |||
| ebe2f2b950 | |||
| 9998946886 | |||
| 0b16a37675 | |||
| f860b5a058 | |||
| 4f982ee3d1 | |||
| 5f4acdee0e | |||
| e640cc187b | |||
| d52fa9bcfc | |||
| 3029f0dd16 | |||
| 9f69d80b12 | |||
| 573e154c18 | |||
| ad49eea20d | |||
| 1dfb8ee3ce | |||
| bca57a4330 | |||
| ea483333f4 | |||
| 9f2d83c148 | |||
| 4a59e203cb | |||
| 697137585e | |||
| 8031ec9d89 | |||
| 54310ba2bc | |||
| 60a894bded | |||
| f40cbb06d6 | |||
| 01576229fe | |||
| 6ad505b6e4 | |||
| 9c79b55078 | |||
| aac231c39a | |||
| 6b577e1516 | |||
| 79b4503d01 | |||
| 1673a59a65 | |||
| ae395070d9 | |||
| eced38791a | |||
| 7d3c3cc1e3 | |||
| a26fdf3b54 | |||
| b162585beb | |||
| 77fb880f2d | |||
| 46222d488e | |||
| c0abe47d87 | |||
| d6321ab2a1 | |||
| ca1cc1c76e | |||
| 92593c3ea9 | |||
| 4b2a89683a | |||
| a830db6d86 | |||
| 7fb62aedc7 | |||
| c7fa954449 | |||
| a002e04838 | |||
| fe38bab63e | |||
| 86e0c29c25 | |||
| e554792ea5 | |||
| a82f60e302 | |||
| 072a7ac84a | |||
| 8dcfd7be61 | |||
| a401afe52e | |||
| f2ccc83a29 | |||
| b0ccf97226 | |||
| 77cccbc405 | |||
| 6370db0587 | |||
| b5c71915c7 | |||
| c96777369d | |||
| 9d663c5819 | |||
| 218eaf92ec | |||
| bbc31e07e9 | |||
| db889737be | |||
| d16ddf42cb | |||
| fa0f0bfad9 | |||
| 23e28fdd93 | |||
| cb62a3b039 | |||
| 0cb15dba0a | |||
| 48112f1a50 | |||
| b99290a060 | |||
| ce013119b9 | |||
| 851643110f | |||
| 45d9f292f9 | |||
| b3723ae020 | |||
| 498bb81c0a | |||
| cfbc1d6b70 | |||
| 7355272818 | |||
| 3c49b63a6b | |||
| db7b0a2272 | |||
| 42e99ac2eb | |||
| 6f86b4ec95 | |||
| 574b492479 | |||
| c9cf188e08 | |||
| b459a204a4 | |||
| a8ca4dbc3a | |||
| 21c4e4940b | |||
| ee6e44669f | |||
| b84cab3fc0 | |||
| a9edcb5f10 | |||
| ee207e2249 | |||
| 28f06953fc | |||
| 90e3024517 | |||
| 6222a3a8f7 | |||
| 14482a174b | |||
| a2395a0417 | |||
| fb2fc2f43f | |||
| dccd6a0499 | |||
| 01ffe94974 | |||
| 67f51b6e69 | |||
| 105eec0693 | |||
| ea4dd30143 | |||
| abfa841a98 | |||
| 1003c372bf | |||
| ff99823130 | |||
| fe431f1e8e | |||
| 2ee49802a9 | |||
| 2ab46f7fa3 | |||
| 77e721f7ce | |||
| d4d1491a81 | |||
| b2b86d2e04 | |||
| 3f3e7fd9f5 | |||
| deec9e9ac8 | |||
| 5634eaaf7a | |||
| 05f8a7bfb7 | |||
| c1aa4fc1d0 | |||
| 51570c73e1 | |||
| 6afafe1114 | |||
| c431a1e701 | |||
| 177103021e | |||
| 5dd4e8ff47 | |||
| 8b36b16b03 | |||
| b04ea483a8 | |||
| 55bb147862 | |||
| f2ba32a950 | |||
| 35c893e84f | |||
| cca9777064 | |||
| 2f386b6be2 | |||
| 6d19312f51 | |||
| a16b8a5474 | |||
| 27b72c4c89 | |||
| dbdce87679 | |||
| 2fd37df46a | |||
| 32a55c5c6e | |||
| 481a3f1ba1 | |||
| 74f6c56d27 | |||
| bcaf8c7e70 | |||
| d1f4eecc3a | |||
| 7f5fef431a | |||
| f6a0f2743e | |||
| 9b49ee6208 | |||
| 198e472ba3 | |||
| 4226ac02dc | |||
| 4606630f5a | |||
| 5d8d561565 | |||
| 6a6ac8b47b | |||
| 6417d7f882 | |||
| 93ee368556 | |||
| d89171768b | |||
| b73037a282 | |||
| 1888ac386f | |||
| 535958a274 | |||
| 35e1f2abed | |||
| 6e5cfde4b1 | |||
| 52828730cb | |||
| e3eae253df | |||
| 104123aa88 | |||
| 71224984bb | |||
| 181f4ab54c | |||
| 5d1f303a16 | |||
| 6a48348831 | |||
| 33be36f175 | |||
| 1a63b68990 | |||
| 1e3963b75d | |||
| ca290eeb97 | |||
| dbe7934931 | |||
| ca12278e90 | |||
| db0e93fa8e | |||
| c8a041fbeb | |||
| 273d14d8a0 | |||
| 024592281f | |||
| 5dbcca1543 | |||
| b57b079c9b |
+45
@@ -0,0 +1,45 @@
|
||||
boost_root
|
||||
.downloads-by-cmake
|
||||
common/netlist_keywords.*
|
||||
common/netlist_lexer.h
|
||||
common/pcb_plot_params_lexer.h
|
||||
common/page_layout/page_layout_reader_keywords.cpp
|
||||
common/fp_lib_table_keywords.*
|
||||
common/gal/opengl/shader_src.h
|
||||
include/fp_lib_table_lexer.h
|
||||
include/netlist_lexer.h
|
||||
include/page_layout_reader_lexer.h
|
||||
eeschema/cmp_library_lexer.h
|
||||
eeschema/cmp_library_keywords.*
|
||||
eeschema/dialogs/dialog_bom_cfg_keywords.cpp
|
||||
eeschema/dialogs/dialog_bom_cfg_lexer.h
|
||||
eeschema/dialogs/dialog_bom_help_html.h
|
||||
eeschema/template_fieldnames_keywords.*
|
||||
eeschema/template_fieldnames_lexer.h
|
||||
pcbnew/dialogs/dialog_freeroute_exchange_help_html.h
|
||||
pcbnew/pcb_plot_params_keywords.cpp
|
||||
pcbnew/pcb_plot_params_lexer.h
|
||||
Makefile
|
||||
CMakeCache.txt
|
||||
auto_renamed_to_cpp
|
||||
Testing
|
||||
version.h
|
||||
config.h
|
||||
install_manifest.txt
|
||||
Documentation/doxygen
|
||||
*.bak
|
||||
common/pcb_plot_params_keywords.cpp
|
||||
include/pcb_plot_params_lexer.h
|
||||
pcbnew/specctra_keywords.cpp
|
||||
pcbnew/specctra_lexer.h
|
||||
pcb_calculator/pcb_calculator_datafile_keywords.cpp
|
||||
pcb_calculator/pcb_calculator_datafile_lexer.h
|
||||
new/html
|
||||
new/sch_lib_table_keywords.cpp
|
||||
new/sch_lib_table_lexer.h
|
||||
new/sweet_keywords.cpp
|
||||
new/sweet_lexer.h
|
||||
bitmaps_png/png*
|
||||
bitmaps_png/tmp
|
||||
common/pcb_keywords.cpp
|
||||
include/pcb_lexer.h
|
||||
@@ -1,5 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
Checks: '-clang-diagnostic-*,clang-analyzer-*, bugprone-*, modernize-deprecated-headers, modernize-loop-convert, modernize-use-override, modernize-redundant-void-arg, modernize-use-emplace, modernize-use-noexcept, modernize-use-bool-literals, modernize-pass-by-value, modernize-use-equals-default, modernize-use-equals-delete, modernize-use-default-member-init, cppcoreguidelines-prefer-member-initializer, cppcoreguidelines-init-variables, cppcoreguidelines-pro-type-member-init'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: '.*'
|
||||
FormatStyle: 'file'
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
# Custom attribute to mark source files using KiCad C++ formatting
|
||||
[attr]kicad-cpp-source text=auto whitepace=tab-in-indent format.clang-format-kicad
|
||||
|
||||
# Custom attribute to mark KiCad's own CMake files
|
||||
[attr]kicad-cmake-source text=auto whitespace=tab-in-indent
|
||||
|
||||
# Custom attribute for auto-generated sources:
|
||||
# * Do not perform whitespace checking
|
||||
# * Do not format
|
||||
[attr]generated whitespace=-tab-in-indent,-indent-with-non-tab -format.clang-format-kicad
|
||||
|
||||
# By default, all C and C++ files conform to KiCad formatting,
|
||||
# unless overridden
|
||||
*.c kicad-cpp-source
|
||||
*.cpp kicad-cpp-source
|
||||
*.h kicad-cpp-source
|
||||
|
||||
*.cmake kicad-cmake-source
|
||||
*.txt kicad-cmake-source
|
||||
*.md text=auto
|
||||
|
||||
# Compiled bitmap sources
|
||||
bitmaps_png/cpp_*/*.cpp generated
|
||||
|
||||
# wxFormBuilder-generated files
|
||||
**/dialog*/*_base.cpp generated
|
||||
**/dialog*/*_base.h generated
|
||||
|
||||
# Lemon grammars
|
||||
common/libeval/grammar.c generated
|
||||
common/libeval/grammar.h generated
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Git "hook chain", used to execute multiple scripts per hook.
|
||||
# To use:
|
||||
# * create a directory called <hookname>.d
|
||||
# * add scripts to this directory (executable)
|
||||
# * ln -s hook-chain <hookname>
|
||||
#
|
||||
# Now the scripts in that directory should be called in order.
|
||||
#
|
||||
# Set $HOOKCHAIN_DEBUG to see the names of invoked scripts.
|
||||
#
|
||||
# Based on script by Oliver Reflalo:
|
||||
# https://stackoverflow.com/questions/8730514/chaining-git-hooks
|
||||
#
|
||||
|
||||
hookname=`basename $0`
|
||||
|
||||
# Temp file for stdin, cleared at exit
|
||||
FILE=`mktemp`
|
||||
trap 'rm -f $FILE' EXIT
|
||||
cat - > $FILE
|
||||
|
||||
# Git hooks directory (this dir)
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
# Execute hooks in the directory one by one
|
||||
for hook in $DIR/$hookname.d/*;
|
||||
do
|
||||
if [ -x "$hook" ]; then
|
||||
|
||||
if [ "$HOOKCHAIN_DEBUG" ]; then
|
||||
echo "Running hook $hook"
|
||||
fi
|
||||
|
||||
cat $FILE | $hook "$@"
|
||||
status=$?
|
||||
|
||||
if [ $status -ne 0 ]; then
|
||||
echo "Hook $hook failed with error code $status"
|
||||
echo "To commit anyway, use --no-verify"
|
||||
exit $status
|
||||
else
|
||||
if [ "$HOOKCHAIN_DEBUG" ]; then
|
||||
echo "Hook passed: $hook"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -1 +0,0 @@
|
||||
hook-chain
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Runs clang-format on changed regions before commit.
|
||||
#
|
||||
# Remaining installation checks/instructions will be printed when you commit.
|
||||
#
|
||||
# Based on clang-format pre-commit hook by Alex Eagle
|
||||
# https://gist.github.com/alexeagle/c8ed91b14a407342d9a8e112b5ac7dab
|
||||
|
||||
# Set kicad.check-format to allow this hook to run
|
||||
# If not set, the hook always succeeds.
|
||||
|
||||
do_format=false
|
||||
|
||||
# Check if formatting is configured
|
||||
if [ "$(git config --get kicad.check-format)" = true ]; then
|
||||
do_format=true
|
||||
fi
|
||||
|
||||
# Older env variable method
|
||||
if [ ! -z "$KICAD_CHECK_FORMAT" ]; then
|
||||
do_format=true
|
||||
fi
|
||||
|
||||
if [ ! ${do_format} = true ]; then
|
||||
# No formatting required
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
check_clang_format() {
|
||||
if hash git-clang-format 2>/dev/null; then
|
||||
return
|
||||
else
|
||||
echo "SETUP ERROR: no git-clang-format executable found, or it is not executable"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_git_config() {
|
||||
if [[ "$(git config --get clangFormat.style)" != "file" ]]; then
|
||||
echo "SETUP ERROR: git config clangFormat.style is wrong (should be 'file'). Fix this with:"
|
||||
echo "git config clangFormat.style file"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_filtered_filenames()
|
||||
{
|
||||
format_attr='clang-format-kicad'
|
||||
|
||||
# command to get list of candidate files
|
||||
git_list_files='git diff --cached --name-only --diff-filter=ACM'
|
||||
|
||||
files=$(${git_list_files} |
|
||||
|
||||
# Filter for format-controlled files
|
||||
git check-attr --stdin format.${format_attr} |
|
||||
|
||||
# output only the file names
|
||||
grep ": set$" |
|
||||
cut -d: -f1)
|
||||
|
||||
echo ${files}
|
||||
}
|
||||
|
||||
check_clang_format
|
||||
check_git_config
|
||||
|
||||
files_to_check=$(get_filtered_filenames)
|
||||
|
||||
if [ -z "${files_to_check}" ]; then
|
||||
# No controlled files to check
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
git_clang_format_cmd="git clang-format -v --diff -- ${files_to_check}"
|
||||
|
||||
readonly out=$(${git_clang_format_cmd})
|
||||
|
||||
# In these cases, there is no formatting issues, so we succeed
|
||||
if [[ "$out" == *"no modified files to format"* ]]; then exit 0; fi
|
||||
if [[ "$out" == *"clang-format did not modify any files"* ]]; then exit 0; fi
|
||||
|
||||
# Any other case implies formatting results
|
||||
echo "ERROR: you need to run clang-format (e.g. using tools/check_coding.sh) on your commit"
|
||||
|
||||
# print the errors to show what's the issue
|
||||
${git_clang_format_cmd}
|
||||
|
||||
# fail the pre-commit
|
||||
exit 1
|
||||
@@ -1,2 +0,0 @@
|
||||
community_bridge: kicad
|
||||
custom: "https://donate.cernandsocietyfoundation.cern/b/my-donation?cid=4&_cv=1"
|
||||
@@ -1,26 +0,0 @@
|
||||
name: 'Redirect PRs'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: opened
|
||||
|
||||
jobs:
|
||||
lockdown:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/repo-lockdown@v2
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
pr-comment: >
|
||||
Thank you for offering to help improve KiCad.
|
||||
This repository is a one-way mirror that can not accept pull requests.
|
||||
Please submit your merge request to the official KiCad repository
|
||||
at https://gitlab.com/kicad/code/kicad/-/merge_requests
|
||||
While we know that _your_ PR is not spam, we mark it as such to dissuade
|
||||
the Hacktoberfest PRs. Please join us over at GitLab. You can even use
|
||||
your GitHub credentials to log in there without creating a new account.
|
||||
skip-closed-pr-comment: true
|
||||
pr-labels: 'spam'
|
||||
close-pr: true
|
||||
lock-pr: true
|
||||
pr-lock-reason: 'spam'
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
.downloads-by-cmake
|
||||
.gdb_history
|
||||
boost_root
|
||||
/Build
|
||||
/build
|
||||
/cmake-build-*
|
||||
common/fp_lib_table_keywords.cpp
|
||||
common/drc_rules_keywords.cpp
|
||||
common/drc_rules_lexer.h
|
||||
common/netlist_keywords.*
|
||||
common/netlist_lexer.h
|
||||
common/pcb_plot_params_lexer.h
|
||||
common/drawing_sheet/drawing_sheet_reader_keywords.cpp
|
||||
common/lib_table_keywords.*
|
||||
common/gal/opengl/shader_src.h
|
||||
include/fp_lib_table_lexer.h
|
||||
include/lib_table_lexer.h
|
||||
include/netlist_lexer.h
|
||||
include/page_layout_reader_lexer.h
|
||||
eeschema/cmp_library_lexer.h
|
||||
eeschema/cmp_library_keywords.*
|
||||
eeschema/dialogs/dialog_bom_cfg_keywords.cpp
|
||||
eeschema/dialogs/dialog_bom_cfg_lexer.h
|
||||
common/template_fieldnames_keywords.cpp
|
||||
common/template_fieldnames_lexer.h
|
||||
eeschema/schematic_keywords.*
|
||||
pcbnew/pcb_plot_params_keywords.cpp
|
||||
pcbnew/pcb_plot_params_lexer.h
|
||||
Makefile
|
||||
CMakeCache.txt
|
||||
.cache/
|
||||
auto_renamed_to_cpp
|
||||
Testing
|
||||
version.h
|
||||
config.h
|
||||
install_manifest.txt
|
||||
doxygen/out
|
||||
*.bak
|
||||
*.pyc
|
||||
common/pcb_plot_params_keywords.cpp
|
||||
include/pcb_plot_params_lexer.h
|
||||
pcbnew/specctra_import_export/specctra_keywords.cpp
|
||||
pcbnew/specctra_import_export/specctra_lexer.h
|
||||
pcb_calculator/pcb_calculator_datafile_keywords.cpp
|
||||
pcb_calculator/pcb_calculator_datafile_lexer.h
|
||||
new/html
|
||||
new/sch_lib_table_keywords.cpp
|
||||
new/sch_lib_table_lexer.h
|
||||
new/sweet_keywords.cpp
|
||||
new/sweet_lexer.h
|
||||
bitmaps_png/tmp
|
||||
common/pcb_keywords.cpp
|
||||
include/pcb_lexer.h
|
||||
fp-info-cache
|
||||
|
||||
# demo project auxiliary files
|
||||
demos/**/*-bak
|
||||
demos/**/_autosave-*
|
||||
demos/**/fp-info-cache
|
||||
|
||||
# MacOS package info created by CMake
|
||||
bitmap2component/Info.plist
|
||||
cvpcb/Info.plist
|
||||
eeschema/Info.plist
|
||||
gerbview/Info.plist
|
||||
kicad/Info.plist
|
||||
pagelayout_editor/Info.plist
|
||||
pcb_calculator/Info.plist
|
||||
pcbnew/Info.plist
|
||||
|
||||
# editor/OS fluff
|
||||
.*.swp
|
||||
*~
|
||||
.idea
|
||||
.DS_Store
|
||||
*.png
|
||||
*.kiface
|
||||
*.o
|
||||
*.o.*
|
||||
*.a
|
||||
*.a.*
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.old
|
||||
*.gch
|
||||
*.orig
|
||||
*.patch
|
||||
|
||||
# These CMake files are wanted
|
||||
!cmake/**/*.cmake
|
||||
|
||||
# Eclipse IDE
|
||||
.settings/
|
||||
.project
|
||||
.cproject
|
||||
.pydevproject
|
||||
__pycache__
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
.editorconfig
|
||||
CMakeSettings.json
|
||||
.vscode/
|
||||
|
||||
# Sublime Text
|
||||
*.sublime-*
|
||||
|
||||
# KDevelop
|
||||
.kdev4/
|
||||
*.kdev4
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
i18n_status.svg
|
||||
i18n_status.csv
|
||||
|
||||
# Project local settings files and backups (in demos and qa tests)
|
||||
*.kicad_prl
|
||||
*-backups
|
||||
|
||||
# QA dataoutput
|
||||
qa/data/**/*.csv
|
||||
|
||||
# Don't actually ignore any of these files, since we need them in the tree
|
||||
!resources/linux/icons/hicolor/**/**/*
|
||||
!resources/linux/icons-nightly/hicolor/**/**/*
|
||||
!CMakeModules/**/*
|
||||
!resources/bitmaps_png/png/**/*
|
||||
|
||||
# Junk temp files generated by MSVC for resource files
|
||||
/resources/msw/RC*
|
||||
@@ -1,46 +0,0 @@
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- report
|
||||
|
||||
default:
|
||||
image:
|
||||
name: registry.gitlab.com/kicad/kicad-ci/source_containers/master/fedora:36
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Test the formatting in a merge request using clang-format
|
||||
##########################################################################
|
||||
# The variable CI_COMMIT_BEFORE_SHA is not available in normal merge requests
|
||||
# so we must build the commit hash ourselves, see:
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/12850
|
||||
test_formatting:
|
||||
stage: test
|
||||
needs: []
|
||||
only:
|
||||
- merge_requests
|
||||
allow_failure: true
|
||||
before_script:
|
||||
# We must manually add the KiCad remote to ensure it is named sensibly
|
||||
- git remote add product https://gitlab.com/kicad/code/kicad.git
|
||||
- git remote add source ${CI_MERGE_REQUEST_SOURCE_PROJECT_URL}.git
|
||||
- git fetch -n product
|
||||
- git fetch -n source
|
||||
# Get the SHAs of the commits
|
||||
- "TARGET_HEAD_SHA=$(git rev-parse product/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME})"
|
||||
- "SOURCE_HEAD_SHA=$(git rev-parse source/${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME})"
|
||||
- "MERGE_BASE_SHA=$(git merge-base ${TARGET_HEAD_SHA} ${SOURCE_HEAD_SHA})"
|
||||
script:
|
||||
- echo "Testing formatting from commit ${MERGE_BASE_SHA}"
|
||||
- ./tools/check_coding.sh --diff --ci --commit ${MERGE_BASE_SHA}
|
||||
|
||||
|
||||
include:
|
||||
- local: '/.gitlab/templates.yml'
|
||||
- local: '/.gitlab/Fedora-Linux-CI.yml'
|
||||
- local: '/.gitlab/Ubuntu-20.04-CI.yml'
|
||||
- local: '/.gitlab/Windows-CI.yml'
|
||||
- local: '/.gitlab/coverity.yml'
|
||||
- local: '/.gitlab/linux-metadata-validate.yml'
|
||||
- local: '/.gitlab/doxygen.yml'
|
||||
@@ -1,165 +0,0 @@
|
||||
##########################################################################
|
||||
# Build KiCad on Fedora and save the results
|
||||
##########################################################################
|
||||
.fedora_build_linux_base:
|
||||
stage: build
|
||||
# Don't tag until we have separate CI for MRs
|
||||
# tags:
|
||||
# - kicad-fedora
|
||||
image: registry.gitlab.com/kicad/kicad-ci/source_containers/master/fedora:37
|
||||
extends: .only_code
|
||||
interruptible: false
|
||||
cache:
|
||||
key: "cache-fedora-linux"
|
||||
paths:
|
||||
- ccache/
|
||||
before_script:
|
||||
# CCache Config
|
||||
- mkdir -p ccache
|
||||
- export CCACHE_BASEDIR=${PWD}
|
||||
- export CCACHE_DIR=${PWD}/ccache
|
||||
script:
|
||||
- mkdir -p build/linux
|
||||
- cd build/linux
|
||||
- cmake
|
||||
-G Ninja
|
||||
-DCMAKE_BUILD_TYPE=QABUILD
|
||||
-DKICAD_STDLIB_LIGHT_DEBUG=ON
|
||||
-DKICAD_SPICE=ON
|
||||
-DKICAD_BUILD_I18N=ON
|
||||
../../
|
||||
- ninja 2>&1 | tee compilation_log.txt
|
||||
- cd ../../
|
||||
artifacts:
|
||||
# Only save the artifacts that are needed for running the tests in the next stage
|
||||
# and the compilation log. The entire build directory is too large to save as an
|
||||
# artifact.
|
||||
expire_in: 2 hrs
|
||||
when: always
|
||||
paths:
|
||||
- build/linux/3d-viewer/
|
||||
- build/linux/pcbnew/pcbnew.py
|
||||
- build/linux/pcbnew/_pcbnew.kiface
|
||||
- build/linux/pcbnew/_pcbnew.so
|
||||
- build/linux/qa/
|
||||
- build/linux/compilation_log.txt
|
||||
exclude:
|
||||
- build/linux/**/*.o
|
||||
- build/linux/**/*.a
|
||||
|
||||
# This split is to ensure we can always use our runner for our jobs
|
||||
fedora_build_linux_kicad:
|
||||
extends: .fedora_build_linux_base
|
||||
tags:
|
||||
- kicad-fedora
|
||||
only:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
fedora_build_linux_public:
|
||||
extends: .fedora_build_linux_base
|
||||
except:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
|
||||
# Upload the compilation log in an easily downloadable form
|
||||
.fedora_report_build_warn:
|
||||
stage: report
|
||||
extends: .only_code
|
||||
when: always
|
||||
script:
|
||||
- echo "Uploading compilation log"
|
||||
- cp build/linux/compilation_log.txt compilation_log.txt
|
||||
artifacts:
|
||||
expire_in: 1 year
|
||||
expose_as: 'Build log'
|
||||
name: "build_log.txt"
|
||||
paths:
|
||||
- compilation_log.txt
|
||||
|
||||
fedora_report_build_warn_kicad:
|
||||
extends: .fedora_report_build_warn
|
||||
tags:
|
||||
- kicad-fedora
|
||||
needs:
|
||||
- job: fedora_build_linux_kicad
|
||||
artifacts: true
|
||||
only:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
fedora_report_build_warn_public:
|
||||
extends: .fedora_report_build_warn
|
||||
needs:
|
||||
- job: fedora_build_linux_public
|
||||
artifacts: true
|
||||
except:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
|
||||
# Report on the metrics of the code
|
||||
.fedora_report_metrics:
|
||||
stage: report
|
||||
extends: .only_code
|
||||
when: always
|
||||
script:
|
||||
- cat build/linux/compilation_log.txt | { grep "warning:" || test $? = 1; } | awk 'END{print "number_of_fedora_warnings "NR}' > metrics.txt
|
||||
- cat metrics.txt
|
||||
artifacts:
|
||||
reports:
|
||||
metrics: metrics.txt
|
||||
|
||||
fedora_report_metrics_kicad:
|
||||
extends: .fedora_report_metrics
|
||||
tags:
|
||||
- kicad-fedora
|
||||
needs:
|
||||
- job: fedora_build_linux_kicad
|
||||
artifacts: true
|
||||
only:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
fedora_report_metrics_public:
|
||||
extends: .fedora_report_metrics
|
||||
needs:
|
||||
- job: fedora_build_linux_public
|
||||
artifacts: true
|
||||
except:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
##########################################################################
|
||||
# Run the code unit tests.
|
||||
##########################################################################
|
||||
.fedora_qa:
|
||||
image: registry.gitlab.com/kicad/kicad-ci/source_containers/master/fedora:37
|
||||
extends:
|
||||
- .unit_test
|
||||
- .only_code
|
||||
parallel:
|
||||
matrix:
|
||||
# The name of the test without the qa_ prefix
|
||||
- TEST: [python, common, gerbview, pcbnew, eeschema, kimath, sexpr, kicad2step]
|
||||
|
||||
fedora_qa_kicad:
|
||||
extends: .fedora_qa
|
||||
tags:
|
||||
- kicad-fedora
|
||||
needs:
|
||||
- job: fedora_build_linux_kicad
|
||||
artifacts: true
|
||||
only:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
|
||||
fedora_qa_public:
|
||||
extends: .fedora_qa
|
||||
needs:
|
||||
- job: fedora_build_linux_public
|
||||
artifacts: true
|
||||
except:
|
||||
variables:
|
||||
- $CI_PROJECT_ROOT_NAMESPACE == "kicad"
|
||||
@@ -1,119 +0,0 @@
|
||||
##########################################################################
|
||||
# Build KiCad on Ubuntu 20.04 and save the results
|
||||
##########################################################################
|
||||
.ubuntu20.04_build:
|
||||
stage: build
|
||||
tags:
|
||||
- kicad-ubuntu20.04
|
||||
image: registry.gitlab.com/kicad/kicad-ci/source_containers/master/ubuntu:20.04
|
||||
only:
|
||||
- branches@kicad/code/kicad
|
||||
- tags@kicad/code/kicad
|
||||
except:
|
||||
- schedules
|
||||
interruptible: false
|
||||
cache:
|
||||
key: "cache-ubuntu20.04-linux"
|
||||
paths:
|
||||
- ccache/
|
||||
before_script:
|
||||
# CCache Config
|
||||
- mkdir -p ccache
|
||||
- export CCACHE_BASEDIR=${PWD}
|
||||
- export CCACHE_DIR=${PWD}/ccache
|
||||
script:
|
||||
- mkdir -p build/linux
|
||||
- cd build/linux
|
||||
- cmake
|
||||
-G Ninja
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DKICAD_STDLIB_LIGHT_DEBUG=ON
|
||||
-DKICAD_SCRIPTING_WXPYTHON=ON
|
||||
-DKICAD_USE_OCC=ON
|
||||
-DKICAD_SPICE=ON
|
||||
-DKICAD_BUILD_I18N=ON
|
||||
-DKICAD_BUILD_PNS_DEBUG_TOOL=ON
|
||||
../../
|
||||
- ninja 2>&1 | tee compilation_log.txt
|
||||
- cd ../../
|
||||
artifacts:
|
||||
# Only save the artifacts that are needed for running the tests in the next stage
|
||||
# and the compilation log. The entire build directory is too large to save as an
|
||||
# artifact.
|
||||
expire_in: 2 hrs
|
||||
when: always
|
||||
paths:
|
||||
- build/linux/3d-viewer/
|
||||
- build/linux/pcbnew/pcbnew.py
|
||||
- build/linux/pcbnew/_pcbnew.kiface
|
||||
- build/linux/pcbnew/_pcbnew.so
|
||||
- build/linux/qa/
|
||||
- build/linux/compilation_log.txt
|
||||
exclude:
|
||||
- build/linux/**/*.o
|
||||
- build/linux/**/*.a
|
||||
|
||||
# Upload the compilation log in an easily downloadable form
|
||||
.ubuntu20.04_report_build_warn:
|
||||
stage: report
|
||||
when: always
|
||||
only:
|
||||
- branches@kicad/code/kicad
|
||||
- tags@kicad/code/kicad
|
||||
except:
|
||||
- schedules
|
||||
needs:
|
||||
- job: ubuntu20.04_build
|
||||
artifacts: true
|
||||
script:
|
||||
- echo "Uploading compilation log"
|
||||
- cp build/linux/compilation_log.txt compilation_log.txt
|
||||
artifacts:
|
||||
expire_in: 1 year
|
||||
expose_as: 'Build log'
|
||||
name: "build_log.txt"
|
||||
paths:
|
||||
- compilation_log.txt
|
||||
|
||||
# Report on the metrics of the code
|
||||
.ubuntu20.04_report_metrics:
|
||||
stage: report
|
||||
when: always
|
||||
only:
|
||||
- branches@kicad/code/kicad
|
||||
- tags@kicad/code/kicad
|
||||
except:
|
||||
- schedules
|
||||
needs:
|
||||
- job: ubuntu20.04_build
|
||||
artifacts: true
|
||||
script:
|
||||
- cat build/linux/compilation_log.txt | { grep "warning:" || test $? = 1; } | awk 'END{print "number_of_ubuntu_20.04_warnings "NR}' > metrics.txt
|
||||
- cat metrics.txt
|
||||
artifacts:
|
||||
reports:
|
||||
metrics: metrics.txt
|
||||
|
||||
##########################################################################
|
||||
# Run the code unit tests.
|
||||
##########################################################################
|
||||
.ubuntu20.04_qa:
|
||||
extends:
|
||||
- .unit_test
|
||||
needs:
|
||||
- job: ubuntu20.04_build
|
||||
artifacts: true
|
||||
tags:
|
||||
- kicad-ubuntu20.04
|
||||
image: registry.gitlab.com/kicad/kicad-ci/source_containers/master/ubuntu:20.04
|
||||
only:
|
||||
- branches@kicad/code/kicad
|
||||
- tags@kicad/code/kicad
|
||||
except:
|
||||
- schedules
|
||||
parallel:
|
||||
matrix:
|
||||
# The name of the test without the qa_ prefix
|
||||
- TEST: [python, common, gerbview, pcbnew, eeschema, kimath, sexpr, kicad2step]
|
||||
@@ -1,47 +0,0 @@
|
||||
##########################################################################
|
||||
# Build KiCad on Windows and save the results
|
||||
##########################################################################
|
||||
win64_build:
|
||||
stage: build
|
||||
tags:
|
||||
- kicad-windows-1809
|
||||
extends: .only_code
|
||||
interruptible: false
|
||||
image: registry.gitlab.com/kicad/kicad-ci/windows-build-image/win1809-msvc:latest
|
||||
variables:
|
||||
VCPKG_BINARY_SOURCES: 'nuget,kicad,readwrite'
|
||||
cache:
|
||||
key: win64-vcpkg-"$CI_COMMIT_REF_SLUG"
|
||||
paths:
|
||||
- build\windows\vcpkg_installed
|
||||
- .vcpkgCache
|
||||
only:
|
||||
- branches@kicad/code/kicad
|
||||
- tags@kicad/code/kicad
|
||||
script:
|
||||
- C:\builder\build.ps1 -Env -Arch x64
|
||||
- $vcpkgCache=Join-Path -Path (Get-Location) -ChildPath ".vcpkgCache";$env:VCPKG_DEFAULT_BINARY_CACHE=$vcpkgCache;New-Item -ItemType Directory -Force -Path $vcpkgCache
|
||||
- nuget.exe sources add -Name kicad -Source "https://nuget.kicad.org:443/v3/index.json"
|
||||
- nuget.exe setapikey $env:KICAD_NUGET_KEY -Source "https://nuget.kicad.org:443/v3/index.json"
|
||||
- mkdir -p build/windows -Force
|
||||
- cd build/windows
|
||||
- cmake `
|
||||
-G "Ninja" `
|
||||
-DCMAKE_TOOLCHAIN_FILE=C:\builder\vcpkg\scripts\buildsystems\vcpkg.cmake `
|
||||
-DCMAKE_BUILD_TYPE=Debug `
|
||||
-DKICAD_SCRIPTING_WXPYTHON=OFF `
|
||||
-DKICAD_USE_OCC=ON `
|
||||
-DKICAD_SPICE=ON `
|
||||
-DKICAD_BUILD_PNS_DEBUG_TOOL=ON `
|
||||
-DKICAD_USE_3DCONNEXION=ON `
|
||||
../../
|
||||
- cmake --build . 2>&1 | tee compilation_log.txt
|
||||
- cd ../../
|
||||
artifacts:
|
||||
# Only save the artifacts that are needed for running the tests in the next stage
|
||||
# and the compilation log. The entire build directory is too large to save as an
|
||||
# artifact.
|
||||
expire_in: 2 hrs
|
||||
when: always
|
||||
paths:
|
||||
- build/windows/compilation_log.txt
|
||||
@@ -1,70 +0,0 @@
|
||||
# This script is responsible for configuring the coverity file from the cache
|
||||
# (e.g. extracting it or updating it if needed)
|
||||
.coverity_cache_prep: &coverity_cache_prep |
|
||||
echo "Downloading MD5 hash of current Coverity Scan version to compare against cache"
|
||||
curl --output cov-analysis-linux64.md5 https://scan.coverity.com/download/linux64 \
|
||||
--form project=$COVERITY_SCAN_PROJECT_NAME \
|
||||
--form token=$COVERITY_SCAN_TOKEN \
|
||||
--form md5=1
|
||||
echo " *cov-analysis-linux64.tgz" >> cov-analysis-linux64.md5
|
||||
(md5sum --ignore-missing -c cov-analysis-linux64.md5) || (
|
||||
echo "Downloading new Coverity Scan version"
|
||||
curl --output cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 \
|
||||
--form project=$COVERITY_SCAN_PROJECT_NAME \
|
||||
--form token=$COVERITY_SCAN_TOKEN
|
||||
)
|
||||
echo "Extracting Coverity Scan"
|
||||
mkdir coverity/
|
||||
tar xzf cov-analysis-linux64.tgz --strip-components=1 -C coverity
|
||||
test -d coverity/bin
|
||||
|
||||
# This script is responsible for tar'ing and submitting the results of the build.
|
||||
# These results will be saved for 1 day if the build fails.
|
||||
.coverity_submit: &coverity_submit |
|
||||
echo "Creating tar file of scan results"
|
||||
tar cfz cov-int.tar.gz cov-int
|
||||
echo "Submitting scan results"
|
||||
export KICAD_VERSION=$(sed 's/[()]//g' kicad_build_version.txt)
|
||||
curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME \
|
||||
--form token=$COVERITY_SCAN_TOKEN \
|
||||
--form email=$GITLAB_USER_EMAIL \
|
||||
--form file=@cov-int.tar.gz \
|
||||
--form version="$KICAD_VERSION" \
|
||||
--form description="$KICAD_VERSION / $CI_COMMIT_TITLE" 2>&1 \
|
||||
| tee curl-response.txt
|
||||
grep -q 'Build successfully submitted' curl-response.txt
|
||||
|
||||
|
||||
Coverity:
|
||||
tags:
|
||||
- coverity
|
||||
stage: build
|
||||
image: registry.gitlab.com/kicad/kicad-ci/source_containers/master/fedora:37
|
||||
only:
|
||||
refs:
|
||||
- schedules
|
||||
variables:
|
||||
- $SCHEDULED_JOB_NAME == "coverity"
|
||||
cache:
|
||||
key: coverity
|
||||
paths:
|
||||
- cov-analysis-linux64.tgz
|
||||
before_script:
|
||||
- export COVERITY_SCAN_PROJECT_NAME="kicad"
|
||||
- test "$(git rev-parse --is-shallow-repository)" = "false" || (git fetch --unshallow)
|
||||
- git fetch origin
|
||||
script:
|
||||
- *coverity_cache_prep
|
||||
- cmake
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
-DKICAD_STDLIB_LIGHT_DEBUG=ON
|
||||
-DKICAD_SCRIPTING_WXPYTHON=ON
|
||||
-DKICAD_SPICE=ON
|
||||
- coverity/bin/cov-build --dir cov-int make -j8
|
||||
- *coverity_submit
|
||||
artifacts:
|
||||
expire_in: 1 year
|
||||
expose_as: 'Coverity log'
|
||||
name: "coverity_log.txt"
|
||||
paths:
|
||||
- cov-int/build-log.txt
|
||||
@@ -1,18 +0,0 @@
|
||||
build_doxygen_docker:
|
||||
image: docker:stable
|
||||
services:
|
||||
- docker:dind
|
||||
stage: build
|
||||
tags:
|
||||
- docker
|
||||
before_script:
|
||||
- docker info
|
||||
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
|
||||
script:
|
||||
- docker build -t "${CI_REGISTRY_IMAGE}/doxygen:$CI_COMMIT_BRANCH" -f ./doxygen/doxygen.Dockerfile .
|
||||
- docker push "${CI_REGISTRY_IMAGE}/doxygen:$CI_COMMIT_BRANCH"
|
||||
only:
|
||||
refs:
|
||||
- schedules
|
||||
variables:
|
||||
- $SCHEDULED_JOB_NAME == "doxygen"
|
||||
@@ -1,39 +0,0 @@
|
||||
##########################################################################
|
||||
# Run a validation of the metadata files for Linux
|
||||
##########################################################################
|
||||
validate_linux_metadata:
|
||||
stage: test
|
||||
needs: []
|
||||
interruptible: false
|
||||
# This is the only CI image with the utilities needed for this test
|
||||
image: registry.gitlab.com/kicad/kicad-ci/source_containers/master/fedora:37
|
||||
# Due to bug https://github.com/hughsie/appstream-glib/issues/381, this doesn't think our description tag
|
||||
# is localized even though it actually is.
|
||||
allow_failure: true
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
- branches
|
||||
- merge_requests
|
||||
- pushes
|
||||
changes:
|
||||
# Only run if the resources, its tests, or the translations are updated
|
||||
- resources/linux/**/*
|
||||
- qa/resources/linux/**/*
|
||||
- translation/**/*
|
||||
except:
|
||||
- schedules
|
||||
# Only build the metadata files in this CI job
|
||||
script:
|
||||
- mkdir -p build/linux
|
||||
- cd build/linux
|
||||
- cmake
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
-DKICAD_STDLIB_LIGHT_DEBUG=ON
|
||||
-DKICAD_SCRIPTING_WXPYTHON=ON
|
||||
-DKICAD_SPICE=ON
|
||||
-DKICAD_BUILD_I18N=ON
|
||||
../../
|
||||
- make metadata
|
||||
- cd ../../
|
||||
- ./qa/resources/linux/verifyMetadataFiles.sh ./ ./build/linux
|
||||
@@ -1,37 +0,0 @@
|
||||
##########################################################################
|
||||
# This will allow the job to run whenever code events trigger it.
|
||||
##########################################################################
|
||||
.only_code:
|
||||
only:
|
||||
- master
|
||||
- branches
|
||||
- merge_requests
|
||||
- pushes
|
||||
except:
|
||||
- schedules
|
||||
|
||||
##########################################################################
|
||||
# Define a template for all the unit tests, since each one is run in a separate
|
||||
# job to make the display nicer.
|
||||
##########################################################################
|
||||
.unit_test:
|
||||
stage: test
|
||||
when: on_success
|
||||
# We define this using a before script because GitLab cannot merge variable
|
||||
# definitions across inheritance (so the variables block from this would disappear)
|
||||
before_script:
|
||||
- export BOOST_TEST_LOGGER='JUNIT,all,test_results.${TEST}.xml:HRF,warning'
|
||||
- export CTEST_OUTPUT_ON_FAILURE=1
|
||||
script:
|
||||
- cd build/linux/qa
|
||||
- ctest -R qa_${TEST}
|
||||
# GitLab supports displaying the results in the GUI through JUNIT artifacts
|
||||
# (https://docs.gitlab.com/ee/ci/junit_test_reports.html)
|
||||
# so we upload the JUNIT results. Note that there is a bug with how paths
|
||||
# are processed in the junit report (https://gitlab.com/gitlab-org/gitlab/issues/23835)
|
||||
# so we can't use a glob and have to list out each hierarchy separately.
|
||||
artifacts:
|
||||
reports:
|
||||
junit:
|
||||
- build/linux/qa/*/*.xml
|
||||
- build/linux/qa/*/*/*.xml
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
|
||||
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_aux.cpp
|
||||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <trigo.h>
|
||||
#include <wxBasePcbFrame.h>
|
||||
|
||||
#include <class_board_design_settings.h>
|
||||
#include <class_zone.h>
|
||||
#include <class_text_mod.h>
|
||||
#include <class_module.h>
|
||||
#include <class_drawsegment.h>
|
||||
#include <class_pcb_text.h>
|
||||
|
||||
#include <3d_viewer.h>
|
||||
#include <3d_canvas.h>
|
||||
#include <info3d_visu.h>
|
||||
#include <trackball.h>
|
||||
|
||||
// Exported function:
|
||||
void Set_Object_Data( std::vector< S3D_VERTEX >& aVertices, double aBiuTo3DUnits );
|
||||
|
||||
|
||||
void S3D_MASTER::Set_Object_Coords( std::vector< S3D_VERTEX >& aVertices )
|
||||
{
|
||||
unsigned ii;
|
||||
|
||||
/* adjust object scale, rotation and offset position */
|
||||
for( ii = 0; ii < aVertices.size(); ii++ )
|
||||
{
|
||||
aVertices[ii].x *= m_MatScale.x;
|
||||
aVertices[ii].y *= m_MatScale.y;
|
||||
aVertices[ii].z *= m_MatScale.z;
|
||||
|
||||
// adjust rotation
|
||||
if( m_MatRotation.x )
|
||||
RotatePoint( &aVertices[ii].y, &aVertices[ii].z, m_MatRotation.x * 10 );
|
||||
|
||||
if( m_MatRotation.y )
|
||||
RotatePoint( &aVertices[ii].z, &aVertices[ii].x, m_MatRotation.y * 10 );
|
||||
|
||||
if( m_MatRotation.z )
|
||||
RotatePoint( &aVertices[ii].x, &aVertices[ii].y, m_MatRotation.z * 10 );
|
||||
|
||||
/* adjust offset position (offset is given in UNIT 3D (0.1 inch) */
|
||||
#define SCALE_3D_CONV ((IU_PER_MILS * 1000) / UNITS3D_TO_UNITSPCB)
|
||||
aVertices[ii].x += m_MatPosition.x * SCALE_3D_CONV;
|
||||
aVertices[ii].y += m_MatPosition.y * SCALE_3D_CONV;
|
||||
aVertices[ii].z += m_MatPosition.z * SCALE_3D_CONV;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Set_Object_Data( std::vector< S3D_VERTEX >& aVertices, double aBiuTo3DUnits )
|
||||
{
|
||||
unsigned ii;
|
||||
GLfloat ax, ay, az, bx, by, bz, nx, ny, nz, r;
|
||||
|
||||
/* ignore faces with less than 3 points */
|
||||
if( aVertices.size() < 3 )
|
||||
return;
|
||||
|
||||
/* calculate normal direction */
|
||||
ax = aVertices[1].x - aVertices[0].x;
|
||||
ay = aVertices[1].y - aVertices[0].y;
|
||||
az = aVertices[1].z - aVertices[0].z;
|
||||
|
||||
bx = aVertices[aVertices.size() - 1].x - aVertices[0].x;
|
||||
by = aVertices[aVertices.size() - 1].y - aVertices[0].y;
|
||||
bz = aVertices[aVertices.size() - 1].z - aVertices[0].z;
|
||||
|
||||
nx = ay * bz - az * by;
|
||||
ny = az * bx - ax * bz;
|
||||
nz = ax * by - ay * bx;
|
||||
|
||||
r = sqrt( nx * nx + ny * ny + nz * nz );
|
||||
|
||||
if( r >= 0.000001 ) /* avoid division by zero */
|
||||
{
|
||||
nx /= r;
|
||||
ny /= r;
|
||||
nz /= r;
|
||||
glNormal3f( nx, ny, nz );
|
||||
}
|
||||
|
||||
/* glBegin/glEnd */
|
||||
switch( aVertices.size() )
|
||||
{
|
||||
case 3:
|
||||
glBegin( GL_TRIANGLES );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
glBegin( GL_QUADS );
|
||||
break;
|
||||
|
||||
default:
|
||||
glBegin( GL_POLYGON );
|
||||
break;
|
||||
}
|
||||
|
||||
/* draw polygon/triangle/quad */
|
||||
for( ii = 0; ii < aVertices.size(); ii++ )
|
||||
{
|
||||
glVertex3d( aVertices[ii].x * aBiuTo3DUnits,
|
||||
aVertices[ii].y * aBiuTo3DUnits,
|
||||
aVertices[ii].z * aBiuTo3DUnits );
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
GLuint EDA_3D_CANVAS::DisplayCubeforTest()
|
||||
{
|
||||
GLuint gllist = glGenLists( 1 );
|
||||
|
||||
glNewList( gllist, GL_COMPILE_AND_EXECUTE );
|
||||
/* draw six faces of a cube */
|
||||
glBegin( GL_QUADS );
|
||||
glNormal3f( 0.0F, 0.0F, 1.0F );
|
||||
glVertex3f( 0.5F, 0.5F, 0.5F ); glVertex3f( -0.5F, 0.5F, 0.5F );
|
||||
glVertex3f( -0.5F, -0.5F, 0.5F ); glVertex3f( 0.5F, -0.5F, 0.5F );
|
||||
|
||||
glNormal3f( 0.0F, 0.0F, -1.0F );
|
||||
glVertex3f( -0.5F, -0.5F, -0.5F ); glVertex3f( -0.5F, 0.5F, -0.5F );
|
||||
glVertex3f( 0.5F, 0.5F, -0.5F ); glVertex3f( 0.5F, -0.5F, -0.5F );
|
||||
|
||||
glNormal3f( 0.0F, 1.0F, 0.0F );
|
||||
glVertex3f( 0.5F, 0.5F, 0.5F ); glVertex3f( 0.5F, 0.5F, -0.5F );
|
||||
glVertex3f( -0.5F, 0.5F, -0.5F ); glVertex3f( -0.5F, 0.5F, 0.5F );
|
||||
|
||||
glNormal3f( 0.0F, -1.0F, 0.0F );
|
||||
glVertex3f( -0.5F, -0.5F, -0.5F ); glVertex3f( 0.5F, -0.5F, -0.5F );
|
||||
glVertex3f( 0.5F, -0.5F, 0.5F ); glVertex3f( -0.5F, -0.5F, 0.5F );
|
||||
|
||||
glNormal3f( 1.0F, 0.0F, 0.0F );
|
||||
glVertex3f( 0.5F, 0.5F, 0.5F ); glVertex3f( 0.5F, -0.5F, 0.5F );
|
||||
glVertex3f( 0.5F, -0.5F, -0.5F ); glVertex3f( 0.5F, 0.5F, -0.5F );
|
||||
|
||||
glNormal3f( -1.0F, 0.0F, 0.0F );
|
||||
glVertex3f( -0.5F, -0.5F, -0.5F ); glVertex3f( -0.5F, -0.5F, 0.5F );
|
||||
glVertex3f( -0.5F, 0.5F, 0.5F ); glVertex3f( -0.5F, 0.5F, -0.5F );
|
||||
glEnd();
|
||||
|
||||
glEndList();
|
||||
|
||||
return gllist;
|
||||
}
|
||||
|
||||
VERTEX_VALUE_CTRL::VERTEX_VALUE_CTRL( wxWindow* aParent, wxBoxSizer* aBoxSizer )
|
||||
{
|
||||
wxString text;
|
||||
|
||||
wxFlexGridSizer* gridSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
|
||||
gridSizer->AddGrowableCol( 1 );
|
||||
gridSizer->SetFlexibleDirection( wxHORIZONTAL );
|
||||
gridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
aBoxSizer->Add( gridSizer, 0, wxEXPAND, 5 );
|
||||
|
||||
wxStaticText* msgtitle = new wxStaticText( aParent, wxID_ANY, wxT( "X:" ) );
|
||||
gridSizer->Add( msgtitle, 0, wxALL , 5 );
|
||||
|
||||
m_XValueCtrl = new wxTextCtrl( aParent, wxID_ANY, wxEmptyString,
|
||||
wxDefaultPosition,wxDefaultSize, 0 );
|
||||
gridSizer->Add( m_XValueCtrl, 0, wxALL|wxEXPAND, 5 );
|
||||
|
||||
msgtitle = new wxStaticText( aParent, wxID_ANY, wxT( "Y:" ), wxDefaultPosition,
|
||||
wxDefaultSize, 0 );
|
||||
gridSizer->Add( msgtitle, 0, wxALL, 5 );
|
||||
|
||||
m_YValueCtrl = new wxTextCtrl( aParent, wxID_ANY, wxEmptyString,
|
||||
wxDefaultPosition, wxDefaultSize, 0 );
|
||||
gridSizer->Add( m_YValueCtrl, 0, wxALL|wxEXPAND, 5 );
|
||||
|
||||
msgtitle = new wxStaticText( aParent, wxID_ANY, wxT( "Z:" ), wxDefaultPosition,
|
||||
wxDefaultSize, 0 );
|
||||
gridSizer->Add( msgtitle, 0, wxALL, 5 );
|
||||
|
||||
m_ZValueCtrl = new wxTextCtrl( aParent, wxID_ANY, wxEmptyString,
|
||||
wxDefaultPosition, wxDefaultSize, 0 );
|
||||
gridSizer->Add( m_ZValueCtrl, 0, wxALL|wxEXPAND, 5 );
|
||||
}
|
||||
|
||||
|
||||
VERTEX_VALUE_CTRL::~VERTEX_VALUE_CTRL()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
S3D_VERTEX VERTEX_VALUE_CTRL::GetValue()
|
||||
{
|
||||
S3D_VERTEX value;
|
||||
double dtmp;
|
||||
|
||||
m_XValueCtrl->GetValue().ToDouble( &dtmp );
|
||||
value.x = dtmp;
|
||||
m_YValueCtrl->GetValue().ToDouble( &dtmp );
|
||||
value.y = dtmp;
|
||||
m_ZValueCtrl->GetValue().ToDouble( &dtmp );
|
||||
value.z = dtmp;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void VERTEX_VALUE_CTRL::SetValue( S3D_VERTEX vertex )
|
||||
{
|
||||
wxString text;
|
||||
|
||||
text.Printf( wxT( "%f" ), vertex.x );
|
||||
m_XValueCtrl->Clear();
|
||||
m_XValueCtrl->AppendText( text );
|
||||
|
||||
text.Printf( wxT( "%f" ), vertex.y );
|
||||
m_YValueCtrl->Clear();
|
||||
m_YValueCtrl->AppendText( text );
|
||||
|
||||
text.Printf( wxT( "%f" ), vertex.z );
|
||||
m_ZValueCtrl->Clear();
|
||||
m_ZValueCtrl->AppendText( text );
|
||||
}
|
||||
|
||||
|
||||
void VERTEX_VALUE_CTRL::Enable( bool onoff )
|
||||
{
|
||||
m_XValueCtrl->Enable( onoff );
|
||||
m_YValueCtrl->Enable( onoff );
|
||||
m_ZValueCtrl->Enable( onoff );
|
||||
}
|
||||
@@ -1,759 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2018-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2022 CERN
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/dir.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/stdpaths.h>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION >= 106800
|
||||
#include <boost/uuid/detail/sha1.hpp>
|
||||
#else
|
||||
#include <boost/uuid/sha1.hpp>
|
||||
#endif
|
||||
|
||||
#include "3d_cache.h"
|
||||
#include "3d_info.h"
|
||||
#include "3d_plugin_manager.h"
|
||||
#include "sg/scenegraph.h"
|
||||
#include "plugins/3dapi/ifsg_api.h"
|
||||
|
||||
#include <advanced_config.h>
|
||||
#include <common.h> // For ExpandEnvVarSubstitutions
|
||||
#include <filename_resolver.h>
|
||||
#include <paths.h>
|
||||
#include <pgm_base.h>
|
||||
#include <project.h>
|
||||
#include <settings/common_settings.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <wx_filename.h>
|
||||
|
||||
|
||||
#define MASK_3D_CACHE "3D_CACHE"
|
||||
|
||||
static std::mutex mutex3D_cache;
|
||||
static std::mutex mutex3D_cacheManager;
|
||||
|
||||
|
||||
static bool isSHA1Same( const unsigned char* shaA, const unsigned char* shaB ) noexcept
|
||||
{
|
||||
for( int i = 0; i < 20; ++i )
|
||||
{
|
||||
if( shaA[i] != shaB[i] )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool checkTag( const char* aTag, void* aPluginMgrPtr )
|
||||
{
|
||||
if( nullptr == aTag || nullptr == aPluginMgrPtr )
|
||||
return false;
|
||||
|
||||
S3D_PLUGIN_MANAGER *pp = (S3D_PLUGIN_MANAGER*) aPluginMgrPtr;
|
||||
|
||||
return pp->CheckTag( aTag );
|
||||
}
|
||||
|
||||
|
||||
static const wxString sha1ToWXString( const unsigned char* aSHA1Sum )
|
||||
{
|
||||
unsigned char uc;
|
||||
unsigned char tmp;
|
||||
char sha1[41];
|
||||
int j = 0;
|
||||
|
||||
for( int i = 0; i < 20; ++i )
|
||||
{
|
||||
uc = aSHA1Sum[i];
|
||||
tmp = uc / 16;
|
||||
|
||||
if( tmp > 9 )
|
||||
tmp += 87;
|
||||
else
|
||||
tmp += 48;
|
||||
|
||||
sha1[j++] = tmp;
|
||||
tmp = uc % 16;
|
||||
|
||||
if( tmp > 9 )
|
||||
tmp += 87;
|
||||
else
|
||||
tmp += 48;
|
||||
|
||||
sha1[j++] = tmp;
|
||||
}
|
||||
|
||||
sha1[j] = 0;
|
||||
|
||||
return wxString::FromUTF8Unchecked( sha1 );
|
||||
}
|
||||
|
||||
|
||||
class S3D_CACHE_ENTRY
|
||||
{
|
||||
public:
|
||||
S3D_CACHE_ENTRY();
|
||||
~S3D_CACHE_ENTRY();
|
||||
|
||||
void SetSHA1( const unsigned char* aSHA1Sum );
|
||||
const wxString GetCacheBaseName();
|
||||
|
||||
wxDateTime modTime; // file modification time
|
||||
unsigned char sha1sum[20];
|
||||
std::string pluginInfo; // PluginName:Version string
|
||||
SCENEGRAPH* sceneData;
|
||||
S3DMODEL* renderData;
|
||||
|
||||
private:
|
||||
// prohibit assignment and default copy constructor
|
||||
S3D_CACHE_ENTRY( const S3D_CACHE_ENTRY& source );
|
||||
S3D_CACHE_ENTRY& operator=( const S3D_CACHE_ENTRY& source );
|
||||
|
||||
wxString m_CacheBaseName; // base name of cache file (a SHA1 digest)
|
||||
};
|
||||
|
||||
|
||||
S3D_CACHE_ENTRY::S3D_CACHE_ENTRY()
|
||||
{
|
||||
sceneData = nullptr;
|
||||
renderData = nullptr;
|
||||
memset( sha1sum, 0, 20 );
|
||||
}
|
||||
|
||||
|
||||
S3D_CACHE_ENTRY::~S3D_CACHE_ENTRY()
|
||||
{
|
||||
delete sceneData;
|
||||
|
||||
if( nullptr != renderData )
|
||||
S3D::Destroy3DModel( &renderData );
|
||||
}
|
||||
|
||||
|
||||
void S3D_CACHE_ENTRY::SetSHA1( const unsigned char* aSHA1Sum )
|
||||
{
|
||||
if( nullptr == aSHA1Sum )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( "%s:%s:%d\n * [BUG] NULL passed for aSHA1Sum" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy( sha1sum, aSHA1Sum, 20 );
|
||||
}
|
||||
|
||||
|
||||
const wxString S3D_CACHE_ENTRY::GetCacheBaseName()
|
||||
{
|
||||
if( m_CacheBaseName.empty() )
|
||||
m_CacheBaseName = sha1ToWXString( sha1sum );
|
||||
|
||||
return m_CacheBaseName;
|
||||
}
|
||||
|
||||
|
||||
S3D_CACHE::S3D_CACHE()
|
||||
{
|
||||
m_FNResolver = new FILENAME_RESOLVER;
|
||||
m_project = nullptr;
|
||||
m_Plugins = new S3D_PLUGIN_MANAGER;
|
||||
}
|
||||
|
||||
|
||||
S3D_CACHE::~S3D_CACHE()
|
||||
{
|
||||
FlushCache();
|
||||
|
||||
delete m_FNResolver;
|
||||
delete m_Plugins;
|
||||
}
|
||||
|
||||
|
||||
SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, const wxString& aBasePath,
|
||||
S3D_CACHE_ENTRY** aCachePtr )
|
||||
{
|
||||
if( aCachePtr )
|
||||
*aCachePtr = nullptr;
|
||||
|
||||
wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile, aBasePath );
|
||||
|
||||
if( full3Dpath.empty() )
|
||||
{
|
||||
// the model cannot be found; we cannot proceed
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( "%s:%s:%d\n * [3D model] could not find model '%s'\n" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aModelFile );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// check cache if file is already loaded
|
||||
std::lock_guard<std::mutex> lock( mutex3D_cache );
|
||||
|
||||
std::map< wxString, S3D_CACHE_ENTRY*, rsort_wxString >::iterator mi;
|
||||
mi = m_CacheMap.find( full3Dpath );
|
||||
|
||||
if( mi != m_CacheMap.end() )
|
||||
{
|
||||
wxFileName fname( full3Dpath );
|
||||
|
||||
if( fname.FileExists() ) // Only check if file exists. If not, it will
|
||||
{ // use the same model in cache.
|
||||
bool reload = ADVANCED_CFG::GetCfg().m_Skip3DModelMemoryCache;
|
||||
wxDateTime fmdate = fname.GetModificationTime();
|
||||
|
||||
if( fmdate != mi->second->modTime )
|
||||
{
|
||||
unsigned char hashSum[20];
|
||||
getSHA1( full3Dpath, hashSum );
|
||||
mi->second->modTime = fmdate;
|
||||
|
||||
if( !isSHA1Same( hashSum, mi->second->sha1sum ) )
|
||||
{
|
||||
mi->second->SetSHA1( hashSum );
|
||||
reload = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( reload )
|
||||
{
|
||||
if( nullptr != mi->second->sceneData )
|
||||
{
|
||||
S3D::DestroyNode( mi->second->sceneData );
|
||||
mi->second->sceneData = nullptr;
|
||||
}
|
||||
|
||||
if( nullptr != mi->second->renderData )
|
||||
S3D::Destroy3DModel( &mi->second->renderData );
|
||||
|
||||
mi->second->sceneData = m_Plugins->Load3DModel( full3Dpath,
|
||||
mi->second->pluginInfo );
|
||||
}
|
||||
}
|
||||
|
||||
if( nullptr != aCachePtr )
|
||||
*aCachePtr = mi->second;
|
||||
|
||||
return mi->second->sceneData;
|
||||
}
|
||||
|
||||
// a cache item does not exist; search the Filename->Cachename map
|
||||
return checkCache( full3Dpath, aCachePtr );
|
||||
}
|
||||
|
||||
|
||||
SCENEGRAPH* S3D_CACHE::Load( const wxString& aModelFile, const wxString& aBasePath )
|
||||
{
|
||||
return load( aModelFile, aBasePath );
|
||||
}
|
||||
|
||||
|
||||
SCENEGRAPH* S3D_CACHE::checkCache( const wxString& aFileName, S3D_CACHE_ENTRY** aCachePtr )
|
||||
{
|
||||
if( aCachePtr )
|
||||
*aCachePtr = nullptr;
|
||||
|
||||
unsigned char sha1sum[20];
|
||||
S3D_CACHE_ENTRY* ep = new S3D_CACHE_ENTRY;
|
||||
m_CacheList.push_back( ep );
|
||||
wxFileName fname( aFileName );
|
||||
ep->modTime = fname.GetModificationTime();
|
||||
|
||||
if( !getSHA1( aFileName, sha1sum ) || m_CacheDir.empty() )
|
||||
{
|
||||
// just in case we can't get a hash digest (for example, on access issues)
|
||||
// or we do not have a configured cache file directory, we create an
|
||||
// entry to prevent further attempts at loading the file
|
||||
|
||||
if( m_CacheMap.emplace( aFileName, ep ).second == false )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( "%s:%s:%d\n * [BUG] duplicate entry in map file; key = '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
m_CacheList.pop_back();
|
||||
delete ep;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aCachePtr )
|
||||
*aCachePtr = ep;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( m_CacheMap.emplace( aFileName, ep ).second == false )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( "%s:%s:%d\n * [BUG] duplicate entry in map file; key = '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
m_CacheList.pop_back();
|
||||
delete ep;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( aCachePtr )
|
||||
*aCachePtr = ep;
|
||||
|
||||
ep->SetSHA1( sha1sum );
|
||||
|
||||
wxString bname = ep->GetCacheBaseName();
|
||||
wxString cachename = m_CacheDir + bname + wxT( ".3dc" );
|
||||
|
||||
if( !ADVANCED_CFG::GetCfg().m_Skip3DModelFileCache && wxFileName::FileExists( cachename )
|
||||
&& loadCacheData( ep ) )
|
||||
return ep->sceneData;
|
||||
|
||||
ep->sceneData = m_Plugins->Load3DModel( aFileName, ep->pluginInfo );
|
||||
|
||||
if( !ADVANCED_CFG::GetCfg().m_Skip3DModelFileCache && nullptr != ep->sceneData )
|
||||
saveCacheData( ep );
|
||||
|
||||
return ep->sceneData;
|
||||
}
|
||||
|
||||
|
||||
bool S3D_CACHE::getSHA1( const wxString& aFileName, unsigned char* aSHA1Sum )
|
||||
{
|
||||
if( aFileName.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( "%s:%s:%d\n * [BUG] empty filename" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( nullptr == aSHA1Sum )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( "%s\n * [BUG] NULL pointer passed for aMD5Sum" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
FILE* fp = _wfopen( aFileName.wc_str(), L"rb" );
|
||||
#else
|
||||
FILE* fp = fopen( aFileName.ToUTF8(), "rb" );
|
||||
#endif
|
||||
|
||||
if( nullptr == fp )
|
||||
return false;
|
||||
|
||||
boost::uuids::detail::sha1 dblock;
|
||||
unsigned char block[4096];
|
||||
size_t bsize = 0;
|
||||
|
||||
while( ( bsize = fread( &block, 1, 4096, fp ) ) > 0 )
|
||||
dblock.process_bytes( block, bsize );
|
||||
|
||||
fclose( fp );
|
||||
unsigned int digest[5];
|
||||
dblock.get_digest( digest );
|
||||
|
||||
// ensure MSB order
|
||||
for( int i = 0; i < 5; ++i )
|
||||
{
|
||||
int idx = i << 2;
|
||||
unsigned int tmp = digest[i];
|
||||
aSHA1Sum[idx+3] = tmp & 0xff;
|
||||
tmp >>= 8;
|
||||
aSHA1Sum[idx+2] = tmp & 0xff;
|
||||
tmp >>= 8;
|
||||
aSHA1Sum[idx+1] = tmp & 0xff;
|
||||
tmp >>= 8;
|
||||
aSHA1Sum[idx] = tmp & 0xff;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D_CACHE::loadCacheData( S3D_CACHE_ENTRY* aCacheItem )
|
||||
{
|
||||
wxString bname = aCacheItem->GetCacheBaseName();
|
||||
|
||||
if( bname.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( " * [3D model] cannot load cached model; no file hash available" ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_CacheDir.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( " * [3D model] cannot load cached model; config directory unknown" ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString fname = m_CacheDir + bname + wxT( ".3dc" );
|
||||
|
||||
if( !wxFileName::FileExists( fname ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( " * [3D model] cannot open file '%s'" ), fname.GetData() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( nullptr != aCacheItem->sceneData )
|
||||
S3D::DestroyNode( (SGNODE*) aCacheItem->sceneData );
|
||||
|
||||
aCacheItem->sceneData = (SCENEGRAPH*)S3D::ReadCache( fname.ToUTF8(), m_Plugins, checkTag );
|
||||
|
||||
if( nullptr == aCacheItem->sceneData )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D_CACHE::saveCacheData( S3D_CACHE_ENTRY* aCacheItem )
|
||||
{
|
||||
if( nullptr == aCacheItem )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( "%s:%s:%d\n * NULL passed for aCacheItem" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( nullptr == aCacheItem->sceneData )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE, wxT( "%s:%s:%d\n * aCacheItem has no valid scene data" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString bname = aCacheItem->GetCacheBaseName();
|
||||
|
||||
if( bname.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( " * [3D model] cannot load cached model; no file hash available" ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_CacheDir.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( " * [3D model] cannot load cached model; config directory unknown" ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString fname = m_CacheDir + bname + wxT( ".3dc" );
|
||||
|
||||
if( wxFileName::Exists( fname ) )
|
||||
{
|
||||
if( !wxFileName::FileExists( fname ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( " * [3D model] path exists but is not a regular file '%s'" ), fname );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return S3D::WriteCache( fname.ToUTF8(), true, (SGNODE*)aCacheItem->sceneData,
|
||||
aCacheItem->pluginInfo.c_str() );
|
||||
}
|
||||
|
||||
|
||||
bool S3D_CACHE::Set3DConfigDir( const wxString& aConfigDir )
|
||||
{
|
||||
if( !m_ConfigDir.empty() )
|
||||
return false;
|
||||
|
||||
wxFileName cfgdir( ExpandEnvVarSubstitutions( aConfigDir, m_project ), wxEmptyString );
|
||||
|
||||
cfgdir.Normalize( FN_NORMALIZE_FLAGS );
|
||||
|
||||
if( !cfgdir.DirExists() )
|
||||
{
|
||||
cfgdir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
|
||||
|
||||
if( !cfgdir.DirExists() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( "%s:%s:%d\n * failed to create 3D configuration directory '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, cfgdir.GetPath() );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_ConfigDir = cfgdir.GetPath();
|
||||
|
||||
// inform the file resolver of the config directory
|
||||
if( !m_FNResolver->Set3DConfigDir( m_ConfigDir ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( "%s:%s:%d\n * could not set 3D Config Directory on filename resolver\n"
|
||||
" * config directory: '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, m_ConfigDir );
|
||||
}
|
||||
|
||||
// 3D cache data must go to a user's cache directory;
|
||||
// unfortunately wxWidgets doesn't seem to provide
|
||||
// functions to retrieve such a directory.
|
||||
//
|
||||
// 1. OSX: ~/Library/Caches/kicad/3d/
|
||||
// 2. Linux: ${XDG_CACHE_HOME}/kicad/3d ~/.cache/kicad/3d/
|
||||
// 3. MSWin: AppData\Local\kicad\3d
|
||||
wxFileName cacheDir;
|
||||
cacheDir.AssignDir( PATHS::GetUserCachePath() );
|
||||
cacheDir.AppendDir( wxT( "3d" ) );
|
||||
|
||||
if( !cacheDir.DirExists() )
|
||||
{
|
||||
cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
|
||||
|
||||
if( !cacheDir.DirExists() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( "%s:%s:%d\n * failed to create 3D cache directory '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_CacheDir = cacheDir.GetPathWithSep();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D_CACHE::SetProject( PROJECT* aProject )
|
||||
{
|
||||
m_project = aProject;
|
||||
|
||||
bool hasChanged = false;
|
||||
|
||||
if( m_FNResolver->SetProject( aProject, &hasChanged ) && hasChanged )
|
||||
{
|
||||
m_CacheMap.clear();
|
||||
|
||||
std::list< S3D_CACHE_ENTRY* >::iterator sL = m_CacheList.begin();
|
||||
std::list< S3D_CACHE_ENTRY* >::iterator eL = m_CacheList.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
delete *sL;
|
||||
++sL;
|
||||
}
|
||||
|
||||
m_CacheList.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void S3D_CACHE::SetProgramBase( PGM_BASE* aBase )
|
||||
{
|
||||
m_FNResolver->SetProgramBase( aBase );
|
||||
}
|
||||
|
||||
|
||||
FILENAME_RESOLVER* S3D_CACHE::GetResolver() noexcept
|
||||
{
|
||||
return m_FNResolver;
|
||||
}
|
||||
|
||||
|
||||
std::list< wxString > const* S3D_CACHE::GetFileFilters() const
|
||||
{
|
||||
return m_Plugins->GetFileFilters();
|
||||
}
|
||||
|
||||
|
||||
void S3D_CACHE::FlushCache( bool closePlugins )
|
||||
{
|
||||
std::list< S3D_CACHE_ENTRY* >::iterator sCL = m_CacheList.begin();
|
||||
std::list< S3D_CACHE_ENTRY* >::iterator eCL = m_CacheList.end();
|
||||
|
||||
while( sCL != eCL )
|
||||
{
|
||||
delete *sCL;
|
||||
++sCL;
|
||||
}
|
||||
|
||||
m_CacheList.clear();
|
||||
m_CacheMap.clear();
|
||||
|
||||
if( closePlugins )
|
||||
ClosePlugins();
|
||||
}
|
||||
|
||||
|
||||
void S3D_CACHE::ClosePlugins()
|
||||
{
|
||||
if( m_Plugins )
|
||||
m_Plugins->ClosePlugins();
|
||||
}
|
||||
|
||||
|
||||
S3DMODEL* S3D_CACHE::GetModel( const wxString& aModelFileName, const wxString& aBasePath )
|
||||
{
|
||||
S3D_CACHE_ENTRY* cp = nullptr;
|
||||
SCENEGRAPH* sp = load( aModelFileName, aBasePath,&cp );
|
||||
|
||||
if( !sp )
|
||||
return nullptr;
|
||||
|
||||
if( !cp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_CACHE,
|
||||
wxT( "%s:%s:%d\n * [BUG] model loaded with no associated S3D_CACHE_ENTRY" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( cp->renderData )
|
||||
return cp->renderData;
|
||||
|
||||
S3DMODEL* mp = S3D::GetModel( sp );
|
||||
cp->renderData = mp;
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
void S3D_CACHE::CleanCacheDir( int aNumDaysOld )
|
||||
{
|
||||
wxDir dir;
|
||||
wxString fileSpec = wxT( "*.3dc" );
|
||||
wxArrayString fileList; // Holds list of ".3dc" files found in cache directory
|
||||
size_t numFilesFound = 0;
|
||||
|
||||
wxFileName thisFile;
|
||||
wxDateTime lastAccess, thresholdDate;
|
||||
wxDateSpan durationInDays;
|
||||
|
||||
// Calc the threshold date above which we delete cache files
|
||||
durationInDays.SetDays( aNumDaysOld );
|
||||
thresholdDate = wxDateTime::Now() - durationInDays;
|
||||
|
||||
// If the cache directory can be found and opened, then we'll try and clean it up
|
||||
if( dir.Open( m_CacheDir ) )
|
||||
{
|
||||
thisFile.SetPath( m_CacheDir ); // Set the base path to the cache folder
|
||||
|
||||
// Get a list of all the ".3dc" files in the cache directory
|
||||
numFilesFound = dir.GetAllFiles( m_CacheDir, &fileList, fileSpec );
|
||||
|
||||
for( unsigned int i = 0; i < numFilesFound; i++ )
|
||||
{
|
||||
// Completes path to specific file so we can get its "last access" date
|
||||
thisFile.SetFullName( fileList[i] );
|
||||
|
||||
// Only get "last access" time to compare against. Don't need the other 2 timestamps.
|
||||
if( thisFile.GetTimes( &lastAccess, nullptr, nullptr ) )
|
||||
{
|
||||
if( lastAccess.IsEarlierThan( thresholdDate ) )
|
||||
{
|
||||
// This file is older than the threshold so delete it
|
||||
wxRemoveFile( thisFile.GetFullPath() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PROJECT::Cleanup3DCache()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( mutex3D_cacheManager );
|
||||
|
||||
// Get the existing cache from the project
|
||||
S3D_CACHE* cache = dynamic_cast<S3D_CACHE*>( GetElem( ELEM_3DCACHE ) );
|
||||
|
||||
if( cache )
|
||||
{
|
||||
// We'll delete ".3dc" cache files older than this many days
|
||||
int clearCacheInterval = 0;
|
||||
|
||||
if( Pgm().GetCommonSettings() )
|
||||
clearCacheInterval = Pgm().GetCommonSettings()->m_System.clear_3d_cache_interval;
|
||||
|
||||
// An interval of zero means the user doesn't want to ever clear the cache
|
||||
|
||||
if( clearCacheInterval > 0 )
|
||||
cache->CleanCacheDir( clearCacheInterval );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
S3D_CACHE* PROJECT::Get3DCacheManager( bool aUpdateProjDir )
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( mutex3D_cacheManager );
|
||||
|
||||
// Get the existing cache from the project
|
||||
S3D_CACHE* cache = dynamic_cast<S3D_CACHE*>( GetElem( ELEM_3DCACHE ) );
|
||||
|
||||
if( !cache )
|
||||
{
|
||||
// Create a cache if there is not one already
|
||||
cache = new S3D_CACHE();
|
||||
|
||||
wxFileName cfgpath;
|
||||
cfgpath.AssignDir( SETTINGS_MANAGER::GetUserSettingsPath() );
|
||||
cfgpath.AppendDir( wxT( "3d" ) );
|
||||
|
||||
cache->SetProgramBase( &Pgm() );
|
||||
cache->Set3DConfigDir( cfgpath.GetFullPath() );
|
||||
|
||||
SetElem( ELEM_3DCACHE, cache );
|
||||
aUpdateProjDir = true;
|
||||
}
|
||||
|
||||
if( aUpdateProjDir )
|
||||
cache->SetProject( this );
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
FILENAME_RESOLVER* PROJECT::Get3DFilenameResolver()
|
||||
{
|
||||
return Get3DCacheManager()->GetResolver();
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_cache.h
|
||||
*/
|
||||
|
||||
#ifndef CACHE_3D_H
|
||||
#define CACHE_3D_H
|
||||
|
||||
#include "3d_info.h"
|
||||
#include <core/typeinfo.h>
|
||||
#include "string_utils.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include "plugins/3dapi/c3dmodel.h"
|
||||
#include <project.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
class PGM_BASE;
|
||||
class S3D_CACHE_ENTRY;
|
||||
class SCENEGRAPH;
|
||||
class FILENAME_RESOLVER;
|
||||
class S3D_PLUGIN_MANAGER;
|
||||
|
||||
|
||||
/**
|
||||
* Cache for storing the 3D shapes. This cache is able to be stored as a project
|
||||
* element (since it inherits from PROJECT::_ELEM).
|
||||
*/
|
||||
class S3D_CACHE : public PROJECT::_ELEM
|
||||
{
|
||||
public:
|
||||
S3D_CACHE();
|
||||
virtual ~S3D_CACHE();
|
||||
|
||||
KICAD_T Type() noexcept override
|
||||
{
|
||||
return S3D_CACHE_T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration directory to be used by the model manager for storing 3D
|
||||
* model manager configuration data and the model cache.
|
||||
*
|
||||
* The config directory may only be set once in the lifetime of the object.
|
||||
*
|
||||
* @param aConfigDir is the configuration directory to use for 3D model manager data
|
||||
* @return true on success
|
||||
*/
|
||||
bool Set3DConfigDir( const wxString& aConfigDir );
|
||||
|
||||
/**
|
||||
* Set the current project's working directory; this affects the model search path.
|
||||
*/
|
||||
bool SetProject( PROJECT* aProject );
|
||||
|
||||
/**
|
||||
* Set the filename resolver's pointer to the application's PGM_BASE instance.
|
||||
*
|
||||
* The pointer is used to extract the local environment variables.
|
||||
*/
|
||||
void SetProgramBase( PGM_BASE* aBase );
|
||||
|
||||
/**
|
||||
* Attempt to load the scene data for a model.
|
||||
*
|
||||
* It will consult the internal cache list and load from cache if possible before invoking
|
||||
* the load() function of the available plugins. The model may fail to load if, for example,
|
||||
* the plugin does not support rendering of the 3D model.
|
||||
|
||||
*
|
||||
* @param aModelFile is the partial or full path to the model to be loaded.
|
||||
* @param aBasePath is the path to search for any relative files
|
||||
* @return true if the model was successfully loaded, otherwise false.
|
||||
*/
|
||||
SCENEGRAPH* Load( const wxString& aModelFile, const wxString& aBasePath );
|
||||
|
||||
FILENAME_RESOLVER* GetResolver() noexcept;
|
||||
|
||||
/**
|
||||
* Return the list of file filters retrieved from the plugins.
|
||||
*
|
||||
* This will contain at least the default "All Files (*.*)|*.*"
|
||||
*
|
||||
* @return a pointer to the filter list.
|
||||
*/
|
||||
std::list< wxString > const* GetFileFilters() const;
|
||||
|
||||
/**
|
||||
* Free all data in the cache and by default closes all plugins.
|
||||
*/
|
||||
void FlushCache( bool closePlugins = true );
|
||||
|
||||
/**
|
||||
* Unload plugins to free memory.
|
||||
*/
|
||||
void ClosePlugins();
|
||||
|
||||
/**
|
||||
* Attempt to load the scene data for a model and to translate it into an S3D_MODEL
|
||||
* structure for display by a renderer.
|
||||
*
|
||||
* @param aModelFileName is the full path to the model to be loaded.
|
||||
* @return is a pointer to the render data or NULL if not available.
|
||||
*/
|
||||
S3DMODEL* GetModel( const wxString& aModelFileName, const wxString& aBasePath );
|
||||
|
||||
/**
|
||||
* Delete up old cache files in cache directory.
|
||||
*
|
||||
* Deletes ".3dc" files in the cache directory that are older than \a aNumDaysOld.
|
||||
*
|
||||
* @param aNumDaysOld is age threshold to delete ".3dc" cache files.
|
||||
*/
|
||||
void CleanCacheDir( int aNumDaysOld );
|
||||
|
||||
private:
|
||||
/**
|
||||
* Find or create cache entry for file name
|
||||
*
|
||||
* Searches the cache list for the given filename and retrieves the cache data; a cache
|
||||
* entry is created if one does not already exist.
|
||||
*
|
||||
* @param aFileName is the file name (full or partial path).
|
||||
* @param aCachePtr is an optional return address for cache entry pointer.
|
||||
* @return SCENEGRAPH object associated with file name or NULL on error.
|
||||
*/
|
||||
SCENEGRAPH* checkCache( const wxString& aFileName, S3D_CACHE_ENTRY** aCachePtr = nullptr );
|
||||
|
||||
/**
|
||||
* Calculate the SHA1 hash of the given file.
|
||||
*
|
||||
* @param aFileName file name (full path).
|
||||
* @param aSHA1Sum a 20 byte character array to hold the SHA1 hash.
|
||||
* @return true on success, otherwise false.
|
||||
*/
|
||||
bool getSHA1( const wxString& aFileName, unsigned char* aSHA1Sum );
|
||||
|
||||
// load scene data from a cache file
|
||||
bool loadCacheData( S3D_CACHE_ENTRY* aCacheItem );
|
||||
|
||||
// save scene data to a cache file
|
||||
bool saveCacheData( S3D_CACHE_ENTRY* aCacheItem );
|
||||
|
||||
// the real load function (can supply a cache entry pointer to member functions)
|
||||
SCENEGRAPH* load( const wxString& aModelFile, const wxString& aBasePath, S3D_CACHE_ENTRY** aCachePtr = nullptr );
|
||||
|
||||
/// cache entries
|
||||
std::list< S3D_CACHE_ENTRY* > m_CacheList;
|
||||
|
||||
/// mapping of file names to cache names and data
|
||||
std::map< wxString, S3D_CACHE_ENTRY*, rsort_wxString > m_CacheMap;
|
||||
|
||||
FILENAME_RESOLVER* m_FNResolver;
|
||||
|
||||
S3D_PLUGIN_MANAGER* m_Plugins;
|
||||
|
||||
PROJECT* m_project;
|
||||
wxString m_CacheDir;
|
||||
wxString m_ConfigDir; /// base configuration path for 3D items
|
||||
};
|
||||
|
||||
#endif // CACHE_3D_H
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2015 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
|
||||
* Copyright (C) 2004 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_info.h
|
||||
* defines the basic data associated with a single 3D model.
|
||||
*/
|
||||
|
||||
#ifndef INFO_3D_H
|
||||
#define INFO_3D_H
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <plugins/3dapi/sg_base.h>
|
||||
|
||||
class FP_3DMODEL;
|
||||
|
||||
struct S3D_INFO
|
||||
{
|
||||
S3D_INFO();
|
||||
S3D_INFO( const FP_3DMODEL& aModel );
|
||||
|
||||
SGPOINT m_Scale; ///< scaling factors for the 3D footprint shape
|
||||
SGPOINT m_Rotation; ///< an X,Y,Z rotation (unit = degrees) for the 3D shape
|
||||
SGPOINT m_Offset; ///< an offset (unit = inch) for the 3D shape
|
||||
wxString m_Filename; ///< The 3D shape filename in 3D library
|
||||
};
|
||||
|
||||
#endif // INFO_3D_H
|
||||
@@ -1,491 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/dynlib.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <paths.h>
|
||||
#include <wx_filename.h>
|
||||
#include "3d_plugin_manager.h"
|
||||
#include "plugins/3d/3d_plugin.h"
|
||||
#include "3d_cache/sg/scenegraph.h"
|
||||
#include "plugins/ldr/3d/pluginldr3D.h"
|
||||
|
||||
|
||||
/**
|
||||
* Flag to enable 3D plugin manager debug tracing.
|
||||
*
|
||||
* Use "KI_TRACE_EDA_3D_VIEWER" to enable.
|
||||
*
|
||||
* @ingroup trace_env_vars
|
||||
*/
|
||||
#define MASK_3D_PLUGINMGR "3D_PLUGIN_MANAGER"
|
||||
|
||||
|
||||
S3D_PLUGIN_MANAGER::S3D_PLUGIN_MANAGER()
|
||||
{
|
||||
// create the initial file filter list entry
|
||||
m_FileFilters.emplace_back( _( "All Files" ) + wxT( " (*.*)|*.*" ) );
|
||||
|
||||
// discover and load plugins
|
||||
loadPlugins();
|
||||
|
||||
#ifdef DEBUG
|
||||
if( !m_ExtMap.empty() )
|
||||
{
|
||||
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator sM = m_ExtMap.begin();
|
||||
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator eM = m_ExtMap.end();
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * Extension [plugin name]:\n" ) );
|
||||
|
||||
while( sM != eM )
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s' [%s]\n" ), sM->first.GetData(),
|
||||
sM->second->GetKicadPluginName() );
|
||||
++sM;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No plugins available\n" ) );
|
||||
}
|
||||
|
||||
|
||||
if( !m_FileFilters.empty() )
|
||||
{
|
||||
/// list of file filters
|
||||
std::list< wxString >::const_iterator sFF = m_FileFilters.begin();
|
||||
std::list< wxString >::const_iterator eFF = m_FileFilters.end();
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * File filters:\n" ) );
|
||||
|
||||
while( sFF != eFF )
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s'\n" ), (*sFF).GetData() );
|
||||
++sFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No file filters available\n" ) );
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
|
||||
S3D_PLUGIN_MANAGER::~S3D_PLUGIN_MANAGER()
|
||||
{
|
||||
std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
|
||||
std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
|
||||
|
||||
while( sP != eP )
|
||||
{
|
||||
(*sP)->Close();
|
||||
delete *sP;
|
||||
++sP;
|
||||
}
|
||||
|
||||
m_Plugins.clear();
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::loadPlugins( void )
|
||||
{
|
||||
std::list<wxString> searchpaths;
|
||||
std::list<wxString> pluginlist;
|
||||
wxFileName fn;
|
||||
|
||||
#ifndef __WXMAC__
|
||||
if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
|
||||
{
|
||||
// set up to work from the build directory
|
||||
fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
|
||||
fn.AppendDir( wxT( ".." ) );
|
||||
fn.AppendDir( wxT( "plugins" ) );
|
||||
fn.AppendDir( wxT( "3d" ) );
|
||||
|
||||
std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() );
|
||||
checkPluginPath( testpath, searchpaths );
|
||||
|
||||
// add subdirectories too
|
||||
wxDir debugPluginDir;
|
||||
wxString subdir;
|
||||
|
||||
debugPluginDir.Open( testpath );
|
||||
|
||||
if( debugPluginDir.IsOpened()
|
||||
&& debugPluginDir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
|
||||
{
|
||||
checkPluginPath( testpath + subdir, searchpaths );
|
||||
|
||||
while( debugPluginDir.GetNext( &subdir ) )
|
||||
checkPluginPath( testpath + subdir, searchpaths );
|
||||
}
|
||||
}
|
||||
|
||||
fn.AssignDir( PATHS::GetStockPlugins3DPath() );
|
||||
checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
|
||||
#else
|
||||
// Search path on OS X is
|
||||
// (1) Machine /Library/Application Support/kicad/PlugIns/3d
|
||||
checkPluginPath( PATHS::GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
|
||||
|
||||
// (2) Bundle kicad.app/Contents/PlugIns/3d
|
||||
fn.AssignDir( PATHS::GetStockPlugins3DPath() );
|
||||
checkPluginPath( fn.GetPathWithSep(), searchpaths );
|
||||
#endif
|
||||
|
||||
std::list< wxString >::iterator sPL = searchpaths.begin();
|
||||
std::list< wxString >::iterator ePL = searchpaths.end();
|
||||
|
||||
while( sPL != ePL )
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] searching path: '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, (*sPL).ToUTF8() );
|
||||
|
||||
listPlugins( *sPL, pluginlist );
|
||||
++sPL;
|
||||
}
|
||||
|
||||
if( pluginlist.empty() )
|
||||
return;
|
||||
|
||||
sPL = pluginlist.begin();
|
||||
ePL = pluginlist.end();
|
||||
|
||||
while( sPL != ePL )
|
||||
{
|
||||
KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
|
||||
|
||||
if( pp->Open( sPL->ToUTF8() ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding plugin" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
m_Plugins.push_back( pp );
|
||||
int nf = pp->GetNFilters();
|
||||
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding %d filters" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, nf );
|
||||
|
||||
for( int i = 0; i < nf; ++i )
|
||||
{
|
||||
char const* cp = pp->GetFileFilter( i );
|
||||
|
||||
if( cp )
|
||||
addFilterString( wxString::FromUTF8Unchecked( cp ) );
|
||||
}
|
||||
|
||||
addExtensionMap( pp );
|
||||
|
||||
// close the loaded library
|
||||
pp->Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] deleting plugin" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
delete pp;
|
||||
}
|
||||
|
||||
++sPL;
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] plugins loaded" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath, std::list< wxString >& aPluginList )
|
||||
{
|
||||
// list potential plugins given a search path
|
||||
wxString nameFilter; // filter for user-loadable libraries (aka footprints)
|
||||
wxString lName; // stores name of enumerated files
|
||||
wxString fName; // full name of file
|
||||
wxDir wd;
|
||||
wd.Open( aPath );
|
||||
|
||||
if( !wd.IsOpened() )
|
||||
return;
|
||||
|
||||
nameFilter = wxT( "*" );
|
||||
|
||||
#ifndef __WXMAC__
|
||||
nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
|
||||
#else
|
||||
// wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
|
||||
// This might be correct, but cmake builds a ".so" for a library MODULE.
|
||||
// Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
|
||||
// bundle being a folder with some special content in it. We obviously don't
|
||||
// want to have that here for our loadable module, so just use ".so".
|
||||
nameFilter.Append( wxS( ".so" ) );
|
||||
#endif
|
||||
|
||||
wxString lp = wd.GetNameWithSep();
|
||||
|
||||
if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
|
||||
{
|
||||
fName = lp + lName;
|
||||
checkPluginName( fName, aPluginList );
|
||||
|
||||
while( wd.GetNext( &lName ) )
|
||||
{
|
||||
fName = lp + lName;
|
||||
checkPluginName( fName, aPluginList );
|
||||
}
|
||||
}
|
||||
|
||||
wd.Close();
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
|
||||
std::list< wxString >& aPluginList )
|
||||
{
|
||||
// check the existence of a plugin name and add it to the list
|
||||
|
||||
if( aPath.empty() || !wxFileName::FileExists( aPath ) )
|
||||
return;
|
||||
|
||||
wxFileName path( ExpandEnvVarSubstitutions( aPath, nullptr ) );
|
||||
|
||||
path.Normalize( FN_NORMALIZE_FLAGS );
|
||||
|
||||
// determine if the path is already in the list
|
||||
wxString wxpath = path.GetFullPath();
|
||||
std::list< wxString >::iterator bl = aPluginList.begin();
|
||||
std::list< wxString >::iterator el = aPluginList.end();
|
||||
|
||||
while( bl != el )
|
||||
{
|
||||
if( 0 == (*bl).Cmp( wxpath ) )
|
||||
return;
|
||||
|
||||
++bl;
|
||||
}
|
||||
|
||||
aPluginList.push_back( wxpath );
|
||||
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] found 3D plugin '%s'\n" ), wxpath.GetData() );
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
|
||||
std::list< wxString >& aSearchList )
|
||||
{
|
||||
if( aPath.empty() )
|
||||
return;
|
||||
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] checking if valid plugin directory '%s'\n" ),
|
||||
aPath.GetData() );
|
||||
|
||||
wxFileName path;
|
||||
path.AssignDir( aPath );
|
||||
path.Normalize( FN_NORMALIZE_FLAGS );
|
||||
|
||||
if( !wxFileName::DirExists( path.GetFullPath() ) )
|
||||
return;
|
||||
|
||||
// determine if the directory is already in the list
|
||||
wxString wxpath = path.GetFullPath();
|
||||
std::list< wxString >::iterator bl = aSearchList.begin();
|
||||
std::list< wxString >::iterator el = aSearchList.end();
|
||||
|
||||
while( bl != el )
|
||||
{
|
||||
if( 0 == (*bl).Cmp( wxpath ) )
|
||||
return;
|
||||
|
||||
++bl;
|
||||
}
|
||||
|
||||
aSearchList.push_back( wxpath );
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
|
||||
{
|
||||
// add an entry to the file filter list
|
||||
if( aFilterString.empty() )
|
||||
return;
|
||||
|
||||
std::list< wxString >::iterator sFF = m_FileFilters.begin();
|
||||
std::list< wxString >::iterator eFF = m_FileFilters.end();
|
||||
|
||||
while( sFF != eFF )
|
||||
{
|
||||
if( 0 == (*sFF).Cmp( aFilterString ) )
|
||||
return;
|
||||
|
||||
++sFF;
|
||||
}
|
||||
|
||||
m_FileFilters.push_back( aFilterString );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin )
|
||||
{
|
||||
// add entries to the extension map
|
||||
if( nullptr == aPlugin )
|
||||
return;
|
||||
|
||||
int nExt = aPlugin->GetNExtensions();
|
||||
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] adding %d extensions" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, nExt );
|
||||
|
||||
for( int i = 0; i < nExt; ++i )
|
||||
{
|
||||
char const* cp = aPlugin->GetModelExtension( i );
|
||||
wxString ws;
|
||||
|
||||
if( cp )
|
||||
ws = wxString::FromUTF8Unchecked( cp );
|
||||
|
||||
if( !ws.empty() )
|
||||
{
|
||||
m_ExtMap.emplace( ws, aPlugin );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const noexcept
|
||||
{
|
||||
return &m_FileFilters;
|
||||
}
|
||||
|
||||
|
||||
SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
|
||||
{
|
||||
wxFileName raw( aFileName );
|
||||
wxString ext_to_find = raw.GetExt();
|
||||
|
||||
#ifdef _WIN32
|
||||
// note: plugins only have a lowercase filter within Windows; including an uppercase
|
||||
// filter will result in duplicate file entries and should be avoided.
|
||||
ext_to_find.MakeLower();
|
||||
#endif
|
||||
|
||||
// .gz files are compressed versions that may have additional information in the previous extension
|
||||
if( ext_to_find == wxT( "gz" ) )
|
||||
{
|
||||
wxFileName second( raw.GetName() );
|
||||
ext_to_find = second.GetExt() + wxT( ".gz" );
|
||||
}
|
||||
|
||||
std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
|
||||
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
|
||||
|
||||
items = m_ExtMap.equal_range( ext_to_find );
|
||||
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
|
||||
|
||||
while( sL != items.second )
|
||||
{
|
||||
if( sL->second->CanRender() )
|
||||
{
|
||||
SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
|
||||
|
||||
if( nullptr != sp )
|
||||
{
|
||||
sL->second->GetPluginInfo( aPluginInfo );
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
|
||||
++sL;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void S3D_PLUGIN_MANAGER::ClosePlugins( void )
|
||||
{
|
||||
std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
|
||||
std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
|
||||
|
||||
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] closing %d extensions" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, static_cast<int>( m_Plugins.size() ) );
|
||||
|
||||
while( sP != eP )
|
||||
{
|
||||
(*sP)->Close();
|
||||
++sP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
|
||||
{
|
||||
if( nullptr == aTag || aTag[0] == 0 || m_Plugins.empty() )
|
||||
return false;
|
||||
|
||||
std::string tname = aTag;
|
||||
std::string pname; // plugin name
|
||||
|
||||
size_t cpos = tname.find( ':' );
|
||||
|
||||
// if there is no colon or plugin name then the tag is bad
|
||||
if( cpos == std::string::npos || cpos == 0 )
|
||||
return false;
|
||||
|
||||
pname = tname.substr( 0, cpos );
|
||||
std::string ptag; // tag from the plugin
|
||||
|
||||
std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
|
||||
std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
|
||||
|
||||
while( pS != pE )
|
||||
{
|
||||
ptag.clear();
|
||||
(*pS)->GetPluginInfo( ptag );
|
||||
|
||||
// if the plugin name matches then the version
|
||||
// must also match
|
||||
if( !ptag.compare( 0, pname.size(), pname ) )
|
||||
{
|
||||
if( ptag.compare( tname ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
++pS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_plugin_manager.h
|
||||
* manages 3D model plugins
|
||||
*/
|
||||
|
||||
#ifndef PLUGIN_MANAGER_3D_H
|
||||
#define PLUGIN_MANAGER_3D_H
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
|
||||
class wxWindow;
|
||||
class KICAD_PLUGIN_LDR_3D;
|
||||
class SCENEGRAPH;
|
||||
|
||||
class S3D_PLUGIN_MANAGER
|
||||
{
|
||||
public:
|
||||
S3D_PLUGIN_MANAGER();
|
||||
virtual ~S3D_PLUGIN_MANAGER();
|
||||
|
||||
/**
|
||||
* Return the list of file filters; this will contain at least the default
|
||||
* "All Files (*.*)|*.*" and the file filters supported by any available plugins.
|
||||
*
|
||||
* @return a pointer to the internal filter list.
|
||||
*/
|
||||
std::list< wxString > const* GetFileFilters( void ) const noexcept;
|
||||
|
||||
SCENEGRAPH* Load3DModel( const wxString& aFileName, std::string& aPluginInfo );
|
||||
|
||||
/**
|
||||
* Iterate through all discovered plugins and closes them to reclaim memory.
|
||||
*
|
||||
* The individual plugins will be automatically reloaded as calls are made to load
|
||||
* specific models.
|
||||
*/
|
||||
void ClosePlugins( void );
|
||||
|
||||
/**
|
||||
* Check the given tag and returns true if the plugin named in the tag is not loaded
|
||||
* or the plugin is loaded and the version matches.
|
||||
*/
|
||||
bool CheckTag( const char* aTag );
|
||||
|
||||
private:
|
||||
/// load plugins
|
||||
void loadPlugins( void );
|
||||
|
||||
/// list potential plugins
|
||||
void listPlugins( const wxString& aPath, std::list< wxString >& aPluginList );
|
||||
|
||||
/// check the existence of a plugin name and add it to the list
|
||||
void checkPluginName( const wxString& aPath, std::list< wxString >& aPluginList );
|
||||
|
||||
/// check the existence of a path and add it to the path search list
|
||||
void checkPluginPath( const wxString& aPath, std::list< wxString >& aSearchList );
|
||||
|
||||
/// add an entry to the file filter list
|
||||
void addFilterString( const wxString& aFilterString );
|
||||
|
||||
/// add entries to the extension map
|
||||
void addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin );
|
||||
|
||||
/// list of discovered plugins
|
||||
std::list< KICAD_PLUGIN_LDR_3D* > m_Plugins;
|
||||
|
||||
/// mapping of extensions to available plugins
|
||||
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* > m_ExtMap;
|
||||
|
||||
/// list of file filters
|
||||
std::list< wxString > m_FileFilters;
|
||||
};
|
||||
|
||||
#endif // PLUGIN_MANAGER_3D_H
|
||||
@@ -1 +0,0 @@
|
||||
add_subdirectory( sg )
|
||||
@@ -1,111 +0,0 @@
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/3d-viewer
|
||||
)
|
||||
|
||||
set( SG_FILES
|
||||
sg_base.cpp
|
||||
sg_node.cpp
|
||||
sg_helpers.cpp
|
||||
scenegraph.cpp
|
||||
sg_appearance.cpp
|
||||
sg_faceset.cpp
|
||||
sg_shape.cpp
|
||||
sg_colors.cpp
|
||||
sg_coords.cpp
|
||||
sg_normals.cpp
|
||||
sg_index.cpp
|
||||
sg_coordindex.cpp
|
||||
ifsg_node.cpp
|
||||
ifsg_transform.cpp
|
||||
ifsg_appearance.cpp
|
||||
ifsg_index.cpp
|
||||
ifsg_coordindex.cpp
|
||||
ifsg_colors.cpp
|
||||
ifsg_coords.cpp
|
||||
ifsg_faceset.cpp
|
||||
ifsg_normals.cpp
|
||||
ifsg_shape.cpp
|
||||
ifsg_api.cpp
|
||||
)
|
||||
|
||||
if( MINGW )
|
||||
list( APPEND SG_FILES ${CMAKE_SOURCE_DIR}/common/streamwrapper.cpp )
|
||||
endif( MINGW )
|
||||
|
||||
add_library( kicad_3dsg SHARED ${SG_FILES} )
|
||||
|
||||
if( APPLE )
|
||||
# puts library into the main kicad.app bundle in build tree
|
||||
set_target_properties( kicad_3dsg PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${OSX_BUNDLE_BUILD_LIB_DIR}"
|
||||
INSTALL_NAME_DIR "${OSX_BUNDLE_BUILD_LIB_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
find_file( S3DSG_VERSION_FILE sg_version.h
|
||||
PATHS ${CMAKE_SOURCE_DIR}/include/plugins/3dapi NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
|
||||
|
||||
if( NOT ${S3DSG_VERSION_FILE} STREQUAL "S3DSG_VERSION_FILE-NOTFOUND" )
|
||||
|
||||
# extract the "#define KICADSG_VERSION_*" lines
|
||||
file( STRINGS ${S3DSG_VERSION_FILE} _version
|
||||
REGEX "^[' ','\t']*#define[' ','\t']*KICADSG_VERSION_.*" )
|
||||
|
||||
foreach( SVAR ${_version} )
|
||||
string( REGEX MATCH KICADSG_VERSION_[M,A,J,O,R,I,N,P,T,C,H,E,V]* _VARNAME ${SVAR} )
|
||||
string( REGEX MATCH [0-9]+ _VALUE ${SVAR} )
|
||||
|
||||
if( NOT ${_VARNAME} STREQUAL "" )
|
||||
if( NOT ${_VALUE} STREQUAL "" )
|
||||
set( ${_VARNAME} ${_VALUE} )
|
||||
else()
|
||||
set( ${_VARNAME} 0 )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
|
||||
if( NOT KICADSG_VERSION_MAJOR AND NOT ${KICADSG_VERSION_MAJOR} STREQUAL "0" )
|
||||
message( FATAL_ERROR "Cannot determine the S3DSG library version" )
|
||||
endif()
|
||||
|
||||
#ensure that NOT SG_VERSION* will evaluate to '0'
|
||||
if( NOT KICADSG_VERSION_MINOR )
|
||||
set( KICADSG_VERSION_MINOR 0 )
|
||||
endif()
|
||||
|
||||
if( NOT KICADSG_VERSION_PATCH )
|
||||
set( KICADSG_VERSION_PATCH 0 )
|
||||
endif()
|
||||
|
||||
set_target_properties( kicad_3dsg
|
||||
PROPERTIES SOVERSION
|
||||
${KICADSG_VERSION_MAJOR}.${KICADSG_VERSION_MINOR}.${KICADSG_VERSION_PATCH} )
|
||||
|
||||
message( STATUS "S3DSG version: ${KICADSG_VERSION_MAJOR}.${KICADSG_VERSION_MINOR}.${KICADSG_VERSION_PATCH}" )
|
||||
else()
|
||||
message( FATAL_ERROR "Cannot determine the S3DSG library version" )
|
||||
endif()
|
||||
|
||||
unset( S3DSG_VERSION_FILE CACHE )
|
||||
|
||||
# Define a flag to expose the appropriate EXPORT macro at build time
|
||||
target_compile_definitions( kicad_3dsg PRIVATE -DCOMPILE_SGLIB )
|
||||
|
||||
target_link_libraries( kicad_3dsg ${wxWidgets_LIBRARIES} )
|
||||
|
||||
# Don't specify the ARCHIVE DESTINATION parameter to prevent
|
||||
# the install of the import library on Windows
|
||||
# https://cmake.org/pipermail/cmake/2011-November/047746.html
|
||||
install( TARGETS
|
||||
kicad_3dsg
|
||||
RUNTIME DESTINATION ${KICAD_LIB}
|
||||
LIBRARY DESTINATION ${KICAD_LIB}
|
||||
COMPONENT binary
|
||||
)
|
||||
|
||||
if( KICAD_WIN32_INSTALL_PDBS )
|
||||
# Get the PDBs to copy over for MSVC
|
||||
install(FILES $<TARGET_PDB_FILE:kicad_3dsg> DESTINATION ${KICAD_BIN})
|
||||
endif()
|
||||
@@ -1,527 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
#include "plugins/3dapi/ifsg_api.h"
|
||||
#include "plugins/3dapi/sg_version.h"
|
||||
#include "streamwrapper.h"
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
#include "3d_cache/sg/scenegraph.h"
|
||||
#include "3d_cache/sg/sg_appearance.h"
|
||||
#include "3d_cache/sg/sg_shape.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
|
||||
|
||||
// version format of the cache file
|
||||
#define SG_VERSION_TAG "VERSION:2"
|
||||
|
||||
|
||||
static void formatMaterial( SMATERIAL& mat, SGAPPEARANCE const* app )
|
||||
{
|
||||
float v0, v1, v2;
|
||||
|
||||
app->ambient.GetColor( v0, v1, v2 );
|
||||
mat.m_Ambient.x = v0;
|
||||
mat.m_Ambient.y = v1;
|
||||
mat.m_Ambient.z = v2;
|
||||
|
||||
app->diffuse.GetColor( v0, v1, v2 );
|
||||
mat.m_Diffuse.x = v0;
|
||||
mat.m_Diffuse.y = v1;
|
||||
mat.m_Diffuse.z = v2;
|
||||
mat.m_Ambient.x *= v0;
|
||||
mat.m_Ambient.y *= v1;
|
||||
mat.m_Ambient.z *= v2;
|
||||
|
||||
app->emissive.GetColor( v0, v1, v2 );
|
||||
mat.m_Emissive.x = v0;
|
||||
mat.m_Emissive.y = v1;
|
||||
mat.m_Emissive.z = v2;
|
||||
|
||||
app->specular.GetColor( v0, v1, v2 );
|
||||
mat.m_Specular.x = v0;
|
||||
mat.m_Specular.y = v1;
|
||||
mat.m_Specular.z = v2;
|
||||
|
||||
mat.m_Shininess = app->shininess;
|
||||
mat.m_Transparency = app->transparency;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::WriteVRML( const char* filename, bool overwrite, SGNODE* aTopNode,
|
||||
bool reuse, bool renameNodes )
|
||||
{
|
||||
if( nullptr == filename || filename[0] == 0 )
|
||||
return false;
|
||||
|
||||
wxString ofile = wxString::FromUTF8Unchecked( filename );
|
||||
|
||||
if( wxFileName::Exists( ofile ) )
|
||||
{
|
||||
if( !overwrite )
|
||||
return false;
|
||||
|
||||
// make sure we make no attempt to write a directory
|
||||
if( !wxFileName::FileExists( ofile ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( aTopNode && aTopNode->GetNodeType() == S3D::SGTYPE_TRANSFORM, false );
|
||||
|
||||
OPEN_OSTREAM( op, filename );
|
||||
|
||||
if( op.fail() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] failed to open file '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, filename );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
op.imbue( std::locale::classic() );
|
||||
op << "#VRML V2.0 utf8\n";
|
||||
|
||||
if( renameNodes )
|
||||
{
|
||||
aTopNode->ResetNodeIndex();
|
||||
aTopNode->ReNameNodes();
|
||||
}
|
||||
|
||||
aTopNode->WriteVRML( op, reuse );
|
||||
|
||||
if( !op.fail() )
|
||||
{
|
||||
CLOSE_STREAM( op );
|
||||
return true;
|
||||
}
|
||||
|
||||
CLOSE_STREAM( op );
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] problems encountered writing file '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, filename );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void S3D::ResetNodeIndex( SGNODE* aNode )
|
||||
{
|
||||
wxCHECK( aNode, /* void */ );
|
||||
|
||||
aNode->ResetNodeIndex();
|
||||
}
|
||||
|
||||
|
||||
void S3D::RenameNodes( SGNODE* aNode )
|
||||
{
|
||||
wxCHECK( aNode, /* void */ );
|
||||
|
||||
aNode->ReNameNodes();
|
||||
}
|
||||
|
||||
|
||||
void S3D::DestroyNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( aNode, /* void */ );
|
||||
|
||||
delete aNode;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::WriteCache( const char* aFileName, bool overwrite, SGNODE* aNode,
|
||||
const char* aPluginInfo )
|
||||
{
|
||||
if( nullptr == aFileName || aFileName[0] == 0 )
|
||||
return false;
|
||||
|
||||
wxString ofile = wxString::FromUTF8Unchecked( aFileName );
|
||||
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
if( wxFileName::Exists( ofile ) )
|
||||
{
|
||||
if( !overwrite )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] file exists not overwriting '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure we make no attempt to write a directory
|
||||
if( !wxFileName::FileExists( aFileName ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] specified path is a directory '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OPEN_OSTREAM( output, aFileName );
|
||||
|
||||
if( output.fail() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] failed to open file '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
output << "(" << SG_VERSION_TAG << ")";
|
||||
|
||||
if( nullptr != aPluginInfo && aPluginInfo[0] != 0 )
|
||||
output << "(" << aPluginInfo << ")";
|
||||
else
|
||||
output << "(INTERNAL:0.0.0.0)";
|
||||
|
||||
bool rval = aNode->WriteCache( output, nullptr );
|
||||
CLOSE_STREAM( output );
|
||||
|
||||
if( !rval )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [INFO] problems encountered writing cache file '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
// delete the defective file
|
||||
wxRemoveFile( ofile );
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* S3D::ReadCache( const char* aFileName, void* aPluginMgr,
|
||||
bool (*aTagCheck)( const char*, void* ) )
|
||||
{
|
||||
if( nullptr == aFileName || aFileName[0] == 0 )
|
||||
return nullptr;
|
||||
|
||||
wxString ofile = wxString::FromUTF8Unchecked( aFileName );
|
||||
|
||||
if( !wxFileName::FileExists( aFileName ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] no such file '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<SGNODE> np = std::make_unique<SCENEGRAPH>( nullptr );
|
||||
|
||||
OPEN_ISTREAM( file, aFileName );
|
||||
|
||||
if( file.fail() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] failed to open file '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aFileName );
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// from SG_VERSION_TAG 1, read the version tag; if it's not the expected tag
|
||||
// then we fail to read the cache file
|
||||
do
|
||||
{
|
||||
std::string name;
|
||||
char schar;
|
||||
file.get( schar );
|
||||
|
||||
if( '(' != schar )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; missing left parenthesis"
|
||||
" at position '%d'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( file.tellg() ) );
|
||||
|
||||
CLOSE_STREAM( file );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.get( schar );
|
||||
|
||||
while( ')' != schar && file.good() )
|
||||
{
|
||||
name.push_back( schar );
|
||||
file.get( schar );
|
||||
}
|
||||
|
||||
if( name.compare( SG_VERSION_TAG ) )
|
||||
{
|
||||
CLOSE_STREAM( file );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} while( 0 );
|
||||
|
||||
// from SG_VERSION_TAG 2, read the PluginInfo string and check that it matches
|
||||
// version tag; if it's not the expected tag then we fail to read the file
|
||||
do
|
||||
{
|
||||
std::string name;
|
||||
char schar;
|
||||
file.get( schar );
|
||||
|
||||
if( '(' != schar )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; missing left parenthesis"
|
||||
" at position '%d'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( file.tellg() ) );
|
||||
|
||||
CLOSE_STREAM( file );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.get( schar );
|
||||
|
||||
while( ')' != schar && file.good() )
|
||||
{
|
||||
name.push_back( schar );
|
||||
file.get( schar );
|
||||
}
|
||||
|
||||
// check the plugin tag
|
||||
if( nullptr != aTagCheck && nullptr != aPluginMgr
|
||||
&& !aTagCheck( name.c_str(), aPluginMgr ) )
|
||||
{
|
||||
CLOSE_STREAM( file );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} while( 0 );
|
||||
|
||||
bool rval = np->ReadCache( file, nullptr );
|
||||
CLOSE_STREAM( file );
|
||||
|
||||
if( !rval )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] problems encountered reading cache file "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aFileName );
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return np.release();
|
||||
}
|
||||
|
||||
|
||||
S3DMODEL* S3D::GetModel( SCENEGRAPH* aNode )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return nullptr;
|
||||
|
||||
if( aNode->GetNodeType() != S3D::SGTYPE_TRANSFORM )
|
||||
return nullptr;
|
||||
|
||||
S3D::MATLIST materials;
|
||||
std::vector< SMESH > meshes;
|
||||
|
||||
// the materials list shall have a default color; although the VRML
|
||||
// default is an opaque black, the default used here shall be a median
|
||||
// gray in hopes that it may help highlight faulty models; this color is
|
||||
// also typical of MCAD applications. When a model has no associated
|
||||
// material color it shall be assigned the index 0.
|
||||
SGAPPEARANCE app( nullptr );
|
||||
app.ambient = SGCOLOR( 0.6f, 0.6f, 0.6f );
|
||||
app.diffuse = SGCOLOR( 0.6f, 0.6f, 0.6f );
|
||||
app.specular = app.diffuse;
|
||||
app.shininess = 0.05f;
|
||||
app.transparency = 0.0f;
|
||||
|
||||
materials.matorder.push_back( &app );
|
||||
materials.matmap.emplace( &app, 0 );
|
||||
|
||||
if( aNode->Prepare( nullptr, materials, meshes ) )
|
||||
{
|
||||
if( meshes.empty() )
|
||||
return nullptr;
|
||||
|
||||
S3DMODEL* model = S3D::New3DModel();
|
||||
|
||||
// add all the materials
|
||||
size_t j = materials.matorder.size();
|
||||
SMATERIAL* lmat = new SMATERIAL[j];
|
||||
|
||||
for( size_t i = 0; i < j; ++i )
|
||||
formatMaterial( lmat[i], materials.matorder[i] );
|
||||
|
||||
model->m_Materials = lmat;
|
||||
model->m_MaterialsSize = j;
|
||||
|
||||
// add all the meshes
|
||||
j = meshes.size();
|
||||
SMESH* lmesh = new SMESH[j];
|
||||
|
||||
for( size_t i = 0; i < j; ++i )
|
||||
lmesh[i] = meshes[i];
|
||||
|
||||
model->m_Meshes = lmesh;
|
||||
model->m_MeshesSize = j;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
size_t j = meshes.size();
|
||||
|
||||
for( size_t i = 0; i < j; ++i )
|
||||
S3D::Free3DMesh( meshes[i] );
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void S3D::Destroy3DModel( S3DMODEL** aModel )
|
||||
{
|
||||
if( nullptr == aModel || nullptr == *aModel )
|
||||
return;
|
||||
|
||||
S3DMODEL* m = *aModel;
|
||||
S3D::FREE_S3DMODEL( *m );
|
||||
delete m;
|
||||
*aModel = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Free3DModel( S3DMODEL& aModel )
|
||||
{
|
||||
S3D::FREE_S3DMODEL( aModel );
|
||||
}
|
||||
|
||||
|
||||
void S3D::Free3DMesh( SMESH& aMesh )
|
||||
{
|
||||
S3D::FREE_SMESH( aMesh );
|
||||
}
|
||||
|
||||
|
||||
S3DMODEL* S3D::New3DModel( void )
|
||||
{
|
||||
S3DMODEL* mp = new S3DMODEL;
|
||||
S3D::INIT_S3DMODEL( *mp );
|
||||
return mp;
|
||||
}
|
||||
|
||||
|
||||
void S3D::Init3DMaterial( SMATERIAL& aMat )
|
||||
{
|
||||
S3D::INIT_SMATERIAL( aMat );
|
||||
}
|
||||
|
||||
|
||||
void S3D::Init3DMesh( SMESH& aMesh )
|
||||
{
|
||||
S3D::INIT_SMESH( aMesh );
|
||||
}
|
||||
|
||||
|
||||
void S3D::GetLibVersion( unsigned char* Major, unsigned char* Minor, unsigned char* Patch,
|
||||
unsigned char* Revision ) noexcept
|
||||
{
|
||||
if( Major )
|
||||
*Major = KICADSG_VERSION_MAJOR;
|
||||
|
||||
if( Minor )
|
||||
*Minor = KICADSG_VERSION_MINOR;
|
||||
|
||||
if( Revision )
|
||||
*Revision = KICADSG_VERSION_REVISION;
|
||||
|
||||
if( Patch )
|
||||
*Patch = KICADSG_VERSION_PATCH;
|
||||
}
|
||||
|
||||
|
||||
SGVECTOR S3D::CalcTriNorm( const SGPOINT& p1, const SGPOINT& p2, const SGPOINT& p3 )
|
||||
{
|
||||
glm::dvec3 tri = glm::dvec3( 0.0, 0.0, 0.0 );
|
||||
glm::dvec3 pts[3];
|
||||
|
||||
pts[0] = glm::dvec3( p1.x, p1.y, p1.z );
|
||||
pts[1] = glm::dvec3( p2.x, p2.y, p2.z );
|
||||
pts[2] = glm::dvec3( p3.x, p3.y, p3.z );
|
||||
|
||||
// degenerate points are given a default 0, 0, 1 normal
|
||||
if( S3D::degenerate( pts ) )
|
||||
return SGVECTOR( 0.0, 0.0, 1.0 );
|
||||
|
||||
// normal
|
||||
tri = glm::cross( pts[1] - pts[0], pts[2] - pts[0] );
|
||||
glm::normalize( tri );
|
||||
|
||||
return SGVECTOR( tri.x, tri.y, tri.z );
|
||||
}
|
||||
|
||||
|
||||
S3D::SGTYPES S3D::GetSGNodeType( SGNODE* aNode )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return SGTYPE_END;
|
||||
|
||||
return aNode->GetNodeType();
|
||||
}
|
||||
|
||||
|
||||
SGNODE* S3D::GetSGNodeParent( SGNODE* aNode )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return nullptr;
|
||||
|
||||
return aNode->GetParent();
|
||||
}
|
||||
|
||||
|
||||
bool S3D::AddSGNodeRef( SGNODE* aParent, SGNODE* aChild )
|
||||
{
|
||||
if( nullptr == aParent || nullptr == aChild )
|
||||
return false;
|
||||
|
||||
return aParent->AddRefNode( aChild );
|
||||
}
|
||||
|
||||
|
||||
bool S3D::AddSGNodeChild( SGNODE* aParent, SGNODE* aChild )
|
||||
{
|
||||
if( nullptr == aParent || nullptr == aChild )
|
||||
return false;
|
||||
|
||||
return aParent->AddChildNode( aChild );
|
||||
}
|
||||
|
||||
|
||||
void S3D::AssociateSGNodeWrapper( SGNODE* aObject, SGNODE** aRefPtr )
|
||||
{
|
||||
if( nullptr == aObject || nullptr == aRefPtr || aObject != *aRefPtr )
|
||||
return;
|
||||
|
||||
aObject->AssociateWrapper( aRefPtr );
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
#include "plugins/3dapi/ifsg_appearance.h"
|
||||
#include "3d_cache/sg/sg_appearance.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_APPEARANCE::IFSG_APPEARANCE( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return ;
|
||||
|
||||
m_node = new SGAPPEARANCE( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_APPEARANCE::IFSG_APPEARANCE( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGAPPEARANCE( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_APPEARANCE::IFSG_APPEARANCE( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
#ifdef DEBUG
|
||||
if( ! pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_node = new SGAPPEARANCE( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_APPEARANCE != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGAPPEARANCE( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGAPPEARANCE" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetEmissive( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetEmissive( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetEmissive( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetEmissive( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetEmissive( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetEmissive( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetDiffuse( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetDiffuse( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetDiffuse( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetDiffuse( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetDiffuse( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetDiffuse( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetSpecular( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetSpecular( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetSpecular( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetSpecular( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetSpecular( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetSpecular( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetAmbient( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetAmbient( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetAmbient( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetAmbient( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetAmbient( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGAPPEARANCE*) m_node )->SetAmbient( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetShininess( float aShininess ) noexcept
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
if( aShininess < 0 || aShininess > 1.0 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] shininess out of range [0..1]" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
( (SGAPPEARANCE*) m_node )->shininess = aShininess;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_APPEARANCE::SetTransparency( float aTransparency ) noexcept
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
if( aTransparency < 0 || aTransparency > 1.0 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] transparency out of range [0..1]" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
( (SGAPPEARANCE*) m_node )->transparency = aTransparency;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_colors.h"
|
||||
#include "3d_cache/sg/sg_colors.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_COLORS::IFSG_COLORS( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return;
|
||||
|
||||
m_node = new SGCOLORS( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_COLORS::IFSG_COLORS( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGCOLORS( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d" ), __FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_COLORS::IFSG_COLORS( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
#ifdef DEBUG
|
||||
// Braces needed due to dangling else warning from wxLogTrace macro
|
||||
if( !pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_node = new SGCOLORS( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_COLORS != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGCOLORS( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGCOLORS" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::GetColorList( size_t& aListSize, SGCOLOR*& aColorList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGCOLORS*) m_node )->GetColorList( aListSize, aColorList );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::SetColorList( size_t aListSize, const SGCOLOR* aColorList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGCOLORS*) m_node )->SetColorList( aListSize, aColorList );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::AddColor( double aRedValue, double aGreenValue, double aBlueValue )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGCOLORS*) m_node )->AddColor( aRedValue, aGreenValue, aBlueValue );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COLORS::AddColor( const SGCOLOR& aColor )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGCOLORS*) m_node )->AddColor( aColor );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_coordindex.h"
|
||||
#include "3d_cache/sg/sg_coordindex.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_COORDINDEX::IFSG_COORDINDEX( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return;
|
||||
|
||||
m_node = new SGCOORDINDEX( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_COORDINDEX::IFSG_COORDINDEX( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGCOORDINDEX( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_COORDINDEX::IFSG_COORDINDEX( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
if( !pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node = new SGCOORDINDEX( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDINDEX::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_COORDINDEX != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDINDEX::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGCOORDINDEX( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGCOORDINDEX" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDINDEX::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_coords.h"
|
||||
#include "3d_cache/sg/sg_coords.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_COORDS::IFSG_COORDS( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return ;
|
||||
|
||||
m_node = new SGCOORDS( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_COORDS::IFSG_COORDS( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGCOORDS( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_COORDS::IFSG_COORDS( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
#ifdef DEBUG
|
||||
if( !pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_node = new SGCOORDS( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_COORDS != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGCOORDS( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGCOORDS" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::GetCoordsList( size_t& aListSize, SGPOINT*& aCoordsList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGCOORDS*) m_node )->GetCoordsList( aListSize, aCoordsList );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::SetCoordsList( size_t aListSize, const SGPOINT* aCoordsList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGCOORDS*) m_node )->SetCoordsList( aListSize, aCoordsList );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::AddCoord( double aXValue, double aYValue, double aZValue )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGCOORDS*) m_node )->AddCoord( aXValue, aYValue, aZValue );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_COORDS::AddCoord( const SGPOINT& aPoint )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGCOORDS*) m_node )->AddCoord( aPoint );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_faceset.h"
|
||||
#include "3d_cache/sg/sg_faceset.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_FACESET::IFSG_FACESET( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return ;
|
||||
|
||||
m_node = new SGFACESET( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_FACESET::IFSG_FACESET( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGFACESET( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_FACESET::IFSG_FACESET( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
#ifdef DEBUG
|
||||
if( ! pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_node = new SGFACESET( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_FACESET::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_FACESET != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_FACESET::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGFACESET( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGFACESET" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_FACESET::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_FACESET::CalcNormals( SGNODE** aPtr )
|
||||
{
|
||||
if( m_node )
|
||||
return ( (SGFACESET*) m_node )->CalcNormals( aPtr );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_index.h"
|
||||
#include "3d_cache/sg/sg_coordindex.h"
|
||||
|
||||
|
||||
IFSG_INDEX::IFSG_INDEX() : IFSG_NODE()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_INDEX::GetIndices( size_t& nIndices, int*& aIndexList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGINDEX*) m_node )->GetIndices( nIndices, aIndexList );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_INDEX::SetIndices( size_t nIndices, int* aIndexList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGINDEX*) m_node )->SetIndices( nIndices, aIndexList );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_INDEX::AddIndex( int aIndex )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGINDEX*) m_node )->AddIndex( aIndex );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_node.h"
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
#include "plugins/3dapi/ifsg_api.h"
|
||||
|
||||
|
||||
// collection of common error strings used by the wrappers
|
||||
char BadObject[] = " * [BUG] operating on an invalid wrapper (object may have been deleted)";
|
||||
char BadOperand[] = " * [BUG] parameter aNode is an invalid wrapper; its data may have been deleted";
|
||||
char BadParent[] = " * [BUG] invalid parent node (data may have been deleted)";
|
||||
char WrongParent[] = " * [BUG] parent node type is incompatible";
|
||||
|
||||
|
||||
IFSG_NODE::IFSG_NODE()
|
||||
{
|
||||
m_node = nullptr;
|
||||
}
|
||||
|
||||
|
||||
IFSG_NODE::~IFSG_NODE()
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
void IFSG_NODE::Destroy( void )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* IFSG_NODE::GetRawPtr( void ) noexcept
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
|
||||
S3D::SGTYPES IFSG_NODE::GetNodeType( void ) const
|
||||
{
|
||||
wxCHECK( m_node, S3D::SGTYPE_END );
|
||||
|
||||
return m_node->GetNodeType();
|
||||
}
|
||||
|
||||
|
||||
SGNODE* IFSG_NODE::GetParent( void ) const
|
||||
{
|
||||
wxCHECK( m_node, nullptr );
|
||||
|
||||
return m_node->GetParent();
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NODE::SetParent( SGNODE* aParent )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return m_node->SetParent( aParent );
|
||||
}
|
||||
|
||||
|
||||
const char* IFSG_NODE::GetName( void )
|
||||
{
|
||||
wxCHECK( m_node, nullptr );
|
||||
|
||||
return m_node->GetName();
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NODE::SetName( const char* aName )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
m_node->SetName( aName );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* IFSG_NODE::GetNodeTypeName( S3D::SGTYPES aNodeType ) const
|
||||
{
|
||||
wxCHECK( m_node, nullptr );
|
||||
|
||||
return m_node->GetNodeTypeName( aNodeType );
|
||||
}
|
||||
|
||||
|
||||
SGNODE* IFSG_NODE::FindNode( const char* aNodeName )
|
||||
{
|
||||
wxCHECK( m_node, nullptr );
|
||||
|
||||
return m_node->FindNode( aNodeName, nullptr );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NODE::AddRefNode( SGNODE* aNode )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return m_node->AddRefNode( aNode );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NODE::AddRefNode( IFSG_NODE& aNode )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
SGNODE* np = aNode.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return m_node->AddRefNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NODE::AddChildNode( SGNODE* aNode )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return m_node->AddChildNode( aNode );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NODE::AddChildNode( IFSG_NODE& aNode )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
SGNODE* np = aNode.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return m_node->AddChildNode( np );
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_normals.h"
|
||||
#include "3d_cache/sg/sg_normals.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_NORMALS::IFSG_NORMALS( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return;
|
||||
|
||||
m_node = new SGNORMALS( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_NORMALS::IFSG_NORMALS( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGNORMALS( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_NORMALS::IFSG_NORMALS( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
#ifdef DEBUG
|
||||
if( ! pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_node = new SGNORMALS( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_NORMALS != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGNORMALS( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGNORMALS" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::GetNormalList( size_t& aListSize, SGVECTOR*& aNormalList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
return ( (SGNORMALS*) m_node )->GetNormalList( aListSize, aNormalList );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::SetNormalList( size_t aListSize, const SGVECTOR* aNormalList )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGNORMALS*) m_node )->SetNormalList( aListSize, aNormalList );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::AddNormal( double aXValue, double aYValue, double aZValue )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGNORMALS*) m_node )->AddNormal( aXValue, aYValue, aZValue );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_NORMALS::AddNormal( const SGVECTOR& aNormal )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SGNORMALS*) m_node )->AddNormal( aNormal );
|
||||
return true;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_shape.h"
|
||||
#include "3d_cache/sg/sg_shape.h"
|
||||
|
||||
|
||||
extern char BadParent[];
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_SHAPE::IFSG_SHAPE( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return ;
|
||||
|
||||
m_node = new SGSHAPE( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_SHAPE::IFSG_SHAPE( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SGSHAPE( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_SHAPE::IFSG_SHAPE( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* pp = aParent.GetRawPtr();
|
||||
|
||||
#ifdef DEBUG
|
||||
if( !pp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
BadParent );
|
||||
}
|
||||
#endif
|
||||
|
||||
m_node = new SGSHAPE( nullptr );
|
||||
|
||||
if( !m_node->SetParent( pp ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_SHAPE::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_SHAPE != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_SHAPE::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SGSHAPE( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SGSHAPE" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_SHAPE::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/ifsg_transform.h"
|
||||
#include "3d_cache/sg/scenegraph.h"
|
||||
|
||||
|
||||
extern char WrongParent[];
|
||||
|
||||
|
||||
IFSG_TRANSFORM::IFSG_TRANSFORM( bool create )
|
||||
{
|
||||
m_node = nullptr;
|
||||
|
||||
if( !create )
|
||||
return;
|
||||
|
||||
m_node = new SCENEGRAPH( nullptr );
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
IFSG_TRANSFORM::IFSG_TRANSFORM( SGNODE* aParent )
|
||||
{
|
||||
m_node = new SCENEGRAPH( nullptr );
|
||||
|
||||
if( !m_node->SetParent( aParent ) )
|
||||
{
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d %s" ), __FILE__, __FUNCTION__, __LINE__,
|
||||
WrongParent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::Attach( SGNODE* aNode )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
if( !aNode )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_TRANSFORM != aNode->GetNodeType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node = aNode;
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::NewNode( SGNODE* aParent )
|
||||
{
|
||||
if( m_node )
|
||||
m_node->DisassociateWrapper( &m_node );
|
||||
|
||||
m_node = new SCENEGRAPH( aParent );
|
||||
|
||||
if( aParent != m_node->GetParent() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid SGNODE parent (%s) to SCENEGRAPH" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeTypeName( aParent->GetNodeType() ) );
|
||||
|
||||
delete m_node;
|
||||
m_node = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_node->AssociateWrapper( &m_node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::NewNode( IFSG_NODE& aParent )
|
||||
{
|
||||
SGNODE* np = aParent.GetRawPtr();
|
||||
|
||||
wxCHECK( np, false );
|
||||
|
||||
return NewNode( np );
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::SetRotation( const SGVECTOR& aRotationAxis, double aAngle )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SCENEGRAPH*) m_node )->rotation_axis = aRotationAxis;
|
||||
( (SCENEGRAPH*) m_node )->rotation_angle = aAngle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::SetScale( const SGPOINT& aScale ) noexcept
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SCENEGRAPH*) m_node )->scale = aScale;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::SetScale( double aScale )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
if( aScale < 1e-8 && aScale > -1e-8 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] |scale| is < 1e-8 - this seems strange" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
( (SCENEGRAPH*) m_node )->scale = SGPOINT( aScale, aScale, aScale );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::SetTranslation( const SGPOINT& aTranslation ) noexcept
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SCENEGRAPH*) m_node )->translation = aTranslation;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::SetScaleOrientation( const SGVECTOR& aScaleAxis, double aAngle )
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SCENEGRAPH*) m_node )->scale_axis = aScaleAxis;
|
||||
( (SCENEGRAPH*) m_node )->scale_angle = aAngle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IFSG_TRANSFORM::SetCenter( const SGPOINT& aCenter ) noexcept
|
||||
{
|
||||
wxCHECK( m_node, false );
|
||||
|
||||
( (SCENEGRAPH*) m_node )->center = aCenter;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,733 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/scenegraph.h"
|
||||
#include "3d_cache/sg/sg_shape.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
|
||||
|
||||
SCENEGRAPH::SCENEGRAPH( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_TRANSFORM;
|
||||
rotation_angle = 0.0;
|
||||
scale_angle = 0.0;
|
||||
|
||||
scale.x = 1.0;
|
||||
scale.y = 1.0;
|
||||
scale.z = 1.0;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_TRANSFORM != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] inappropriate parent to SCENEGRAPH (type %d)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_TRANSFORM == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SCENEGRAPH::~SCENEGRAPH()
|
||||
{
|
||||
// drop references
|
||||
DROP_REFS( SCENEGRAPH, m_RTransforms );
|
||||
DROP_REFS( SGSHAPE, m_RShape );
|
||||
|
||||
// delete owned objects
|
||||
DEL_OBJS( SCENEGRAPH, m_Transforms );
|
||||
DEL_OBJS( SGSHAPE, m_Shape );
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a transform may be parent to a transform
|
||||
if( nullptr != aParent && S3D::SGTYPE_TRANSFORM != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SCENEGRAPH::FindNode(const char *aNodeName, const SGNODE *aCaller)
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
FIND_NODE( SCENEGRAPH, aNodeName, m_Transforms, aCaller );
|
||||
FIND_NODE( SGSHAPE, aNodeName, m_Shape, aCaller );
|
||||
|
||||
// query the parent if appropriate
|
||||
if( aCaller == m_Parent || nullptr == m_Parent )
|
||||
return nullptr;
|
||||
|
||||
return m_Parent->FindNode( aNodeName, this );
|
||||
}
|
||||
|
||||
|
||||
void SCENEGRAPH::unlinkNode( const SGNODE* aNode, bool isChild )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return;
|
||||
|
||||
switch( aNode->GetNodeType() )
|
||||
{
|
||||
case S3D::SGTYPE_TRANSFORM:
|
||||
UNLINK_NODE( S3D::SGTYPE_TRANSFORM, SCENEGRAPH, aNode, m_Transforms, m_RTransforms,
|
||||
isChild );
|
||||
break;
|
||||
|
||||
case S3D::SGTYPE_SHAPE:
|
||||
UNLINK_NODE( S3D::SGTYPE_SHAPE, SGSHAPE, aNode, m_Shape, m_RShape, isChild );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] unlinkNode() did not find its target" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
}
|
||||
|
||||
|
||||
void SCENEGRAPH::unlinkChildNode( const SGNODE* aNode )
|
||||
{
|
||||
unlinkNode( aNode, true );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void SCENEGRAPH::unlinkRefNode( const SGNODE* aNode )
|
||||
{
|
||||
unlinkNode( aNode, false );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::addNode( SGNODE* aNode, bool isChild )
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
ADD_NODE( S3D::SGTYPE_TRANSFORM, SCENEGRAPH, aNode, m_Transforms, m_RTransforms, isChild );
|
||||
ADD_NODE( S3D::SGTYPE_SHAPE, SGSHAPE, aNode, m_Shape, m_RShape, isChild );
|
||||
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] object '%s' is not a valid type for this object (%d)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aNode->GetName(),
|
||||
aNode->GetNodeType() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::AddRefNode( SGNODE* aNode )
|
||||
{
|
||||
return addNode( aNode, false );
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::AddChildNode( SGNODE* aNode )
|
||||
{
|
||||
return addNode( aNode, true );
|
||||
}
|
||||
|
||||
|
||||
void SCENEGRAPH::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
|
||||
// rename all shapes
|
||||
do
|
||||
{
|
||||
std::vector< SGSHAPE* >::iterator sL = m_Shape.begin();
|
||||
std::vector< SGSHAPE* >::iterator eL = m_Shape.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
(*sL)->ReNameNodes();
|
||||
++sL;
|
||||
}
|
||||
|
||||
} while( 0 );
|
||||
|
||||
// rename all transforms
|
||||
do
|
||||
{
|
||||
std::vector< SCENEGRAPH* >::iterator sL = m_Transforms.begin();
|
||||
std::vector< SCENEGRAPH* >::iterator eL = m_Transforms.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
(*sL)->ReNameNodes();
|
||||
++sL;
|
||||
}
|
||||
|
||||
} while( 0 );
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( m_Transforms.empty() && m_RTransforms.empty() && m_Shape.empty() && m_RShape.empty() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string tmp;
|
||||
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << "DEF " << GetName() << " Transform {\n";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << "USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " Transform {\n";
|
||||
}
|
||||
|
||||
// convert center to 1VRML unit = 0.1 inch
|
||||
SGPOINT pt = center;
|
||||
pt.x /= 2.54;
|
||||
pt.y /= 2.54;
|
||||
pt.z /= 2.54;
|
||||
|
||||
S3D::FormatPoint( tmp, pt );
|
||||
aFile << " center " << tmp << "\n";
|
||||
S3D::FormatOrientation( tmp, rotation_axis, rotation_angle );
|
||||
aFile << " rotation " << tmp << "\n";
|
||||
S3D::FormatPoint( tmp, scale );
|
||||
aFile << " scale " << tmp << "\n";
|
||||
S3D::FormatOrientation( tmp, scale_axis, scale_angle );
|
||||
aFile << " scaleOrientation " << tmp << "\n";
|
||||
|
||||
// convert translation to 1VRML unit = 0.1 inch
|
||||
pt = translation;
|
||||
pt.x /= 2.54;
|
||||
pt.y /= 2.54;
|
||||
pt.z /= 2.54;
|
||||
S3D::FormatPoint( tmp, pt );
|
||||
aFile << " translation " << tmp << "\n";
|
||||
|
||||
aFile << " children [\n";
|
||||
|
||||
if( !m_Transforms.empty() )
|
||||
{
|
||||
std::vector< SCENEGRAPH* >::iterator sL = m_Transforms.begin();
|
||||
std::vector< SCENEGRAPH* >::iterator eL = m_Transforms.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
(*sL)->WriteVRML( aFile, aReuseFlag );
|
||||
++sL;
|
||||
}
|
||||
}
|
||||
|
||||
if( !m_RTransforms.empty() )
|
||||
{
|
||||
std::vector< SCENEGRAPH* >::iterator sL = m_RTransforms.begin();
|
||||
std::vector< SCENEGRAPH* >::iterator eL = m_RTransforms.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
(*sL)->WriteVRML( aFile, aReuseFlag );
|
||||
++sL;
|
||||
}
|
||||
}
|
||||
|
||||
if( !m_Shape.empty() )
|
||||
{
|
||||
std::vector< SGSHAPE* >::iterator sL = m_Shape.begin();
|
||||
std::vector< SGSHAPE* >::iterator eL = m_Shape.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
(*sL)->WriteVRML( aFile, aReuseFlag );
|
||||
++sL;
|
||||
}
|
||||
}
|
||||
|
||||
if( !m_RShape.empty() )
|
||||
{
|
||||
std::vector< SGSHAPE* >::iterator sL = m_RShape.begin();
|
||||
std::vector< SGSHAPE* >::iterator eL = m_RShape.end();
|
||||
|
||||
while( sL != eL )
|
||||
{
|
||||
(*sL)->WriteVRML( aFile, aReuseFlag );
|
||||
++sL;
|
||||
}
|
||||
}
|
||||
|
||||
aFile << "] }\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode && nullptr != m_Parent )
|
||||
{
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( nullptr == m_Parent )
|
||||
{
|
||||
// ensure unique node names
|
||||
ResetNodeIndex();
|
||||
ReNameNodes();
|
||||
}
|
||||
|
||||
if( aFile.fail() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
S3D::WritePoint( aFile, center );
|
||||
S3D::WritePoint( aFile, translation );
|
||||
S3D::WriteVector( aFile, rotation_axis );
|
||||
aFile.write( (char*)&rotation_angle, sizeof( rotation_angle ) );
|
||||
S3D::WritePoint( aFile, scale );
|
||||
S3D::WriteVector( aFile, scale_axis );
|
||||
aFile.write( (char*)&scale_angle, sizeof( scale_angle ) );
|
||||
|
||||
// Transfer ownership of any Transform references which hadn't been written
|
||||
size_t asize = m_RTransforms.size();
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < asize; ++i )
|
||||
{
|
||||
if( !m_RTransforms[i]->isWritten() )
|
||||
{
|
||||
m_RTransforms[i]->SwapParent( this );
|
||||
--asize;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer ownership of any Shape references which hadn't been written
|
||||
asize = m_RShape.size();
|
||||
|
||||
for( i = 0; i < asize; ++i )
|
||||
{
|
||||
if( !m_RShape[i]->isWritten() )
|
||||
{
|
||||
m_RShape[i]->SwapParent( this );
|
||||
--asize;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
asize = m_Transforms.size();
|
||||
aFile.write( (char*)&asize, sizeof( size_t ) );
|
||||
asize = m_RTransforms.size();
|
||||
aFile.write( (char*)&asize, sizeof( size_t ) );
|
||||
asize = m_Shape.size();
|
||||
aFile.write( (char*)&asize, sizeof( size_t ) );
|
||||
asize = m_RShape.size();
|
||||
aFile.write( (char*)&asize, sizeof( size_t ) );
|
||||
asize = m_Transforms.size();
|
||||
|
||||
// write child transforms
|
||||
for( i = 0; i < asize; ++i )
|
||||
{
|
||||
if( !m_Transforms[i]->WriteCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [INFO] bad stream while writing child transforms" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// write referenced transform names
|
||||
asize = m_RTransforms.size();
|
||||
|
||||
for( i = 0; i < asize; ++i )
|
||||
aFile << "[" << m_RTransforms[i]->GetName() << "]";
|
||||
|
||||
// write child shapes
|
||||
asize = m_Shape.size();
|
||||
|
||||
for( i = 0; i < asize; ++i )
|
||||
{
|
||||
if( !m_Shape[i]->WriteCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [INFO] bad stream while writing child shapes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// write referenced transform names
|
||||
asize = m_RShape.size();
|
||||
|
||||
for( i = 0; i < asize; ++i )
|
||||
aFile << "[" << m_RShape[i]->GetName() << "]";
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
wxCHECK( m_Transforms.empty() && m_RTransforms.empty() && m_Shape.empty() && m_RShape.empty(),
|
||||
false );
|
||||
|
||||
std::string name; // name of the node
|
||||
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
// we need to read the tag and verify its type
|
||||
if( S3D::SGTYPE_TRANSFORM != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; tag mismatch at position "
|
||||
"%ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Name = name;
|
||||
}
|
||||
|
||||
// read fixed member data
|
||||
S3D::ReadPoint( aFile, center );
|
||||
S3D::ReadPoint( aFile, translation );
|
||||
S3D::ReadVector( aFile, rotation_axis );
|
||||
aFile.read( (char*)&rotation_angle, sizeof( rotation_angle ) );
|
||||
S3D::ReadPoint( aFile, scale );
|
||||
S3D::ReadVector( aFile, scale_axis );
|
||||
aFile.read( (char*)&scale_angle, sizeof( scale_angle ) );
|
||||
|
||||
size_t sizeCT = 0; // child transforms
|
||||
size_t sizeRT = 0; // referenced transforms
|
||||
size_t sizeCS = 0; // child shapes
|
||||
size_t sizeRS = 0; // referenced shapes
|
||||
|
||||
aFile.read( (char*)&sizeCT, sizeof( size_t ) );
|
||||
aFile.read( (char*)&sizeRT, sizeof( size_t ) );
|
||||
aFile.read( (char*)&sizeCS, sizeof( size_t ) );
|
||||
aFile.read( (char*)&sizeRS, sizeof( size_t ) );
|
||||
|
||||
size_t i;
|
||||
|
||||
// read child transforms
|
||||
for( i = 0; i < sizeCT; ++i )
|
||||
{
|
||||
if( S3D::SGTYPE_TRANSFORM != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child transform tag "
|
||||
"at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SCENEGRAPH* sp = new SCENEGRAPH( this );
|
||||
sp->SetName( name.c_str() );
|
||||
|
||||
if( !sp->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data while reading transform "
|
||||
"%ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// read referenced transforms
|
||||
for( i = 0; i < sizeRT; ++i )
|
||||
{
|
||||
if( S3D::SGTYPE_TRANSFORM != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref transform tag at "
|
||||
"position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* sp = FindNode( name.c_str(), this );
|
||||
|
||||
if( !sp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: cannot find ref "
|
||||
"transform at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_TRANSFORM != sp->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: type is not TRANSFORM "
|
||||
"at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AddRefNode( sp );
|
||||
}
|
||||
|
||||
// read child shapes
|
||||
for( i = 0; i < sizeCS; ++i )
|
||||
{
|
||||
if( S3D::SGTYPE_SHAPE != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child shape tag at "
|
||||
"position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGSHAPE* sp = new SGSHAPE( this );
|
||||
sp->SetName( name.c_str() );
|
||||
|
||||
if( !sp->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; corrupt data while "
|
||||
"reading shape at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// read referenced shapes
|
||||
for( i = 0; i < sizeRS; ++i )
|
||||
{
|
||||
if( S3D::SGTYPE_SHAPE != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref shape tag at "
|
||||
"position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* sp = FindNode( name.c_str(), this );
|
||||
|
||||
if( !sp )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: cannot find ref shape "
|
||||
"at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_SHAPE != sp->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: type is not SGSHAPE at "
|
||||
"position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AddRefNode( sp );
|
||||
}
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SCENEGRAPH::Prepare( const glm::dmat4* aTransform, S3D::MATLIST& materials,
|
||||
std::vector< SMESH >& meshes )
|
||||
{
|
||||
// calculate the accumulated transform
|
||||
double rX, rY, rZ;
|
||||
|
||||
// rotation
|
||||
rotation_axis.GetVector( rX, rY, rZ );
|
||||
glm::dmat4 rM = glm::rotate( glm::dmat4( 1.0 ), rotation_angle, glm::dvec3( rX, rY, rZ ) );
|
||||
|
||||
// translation
|
||||
glm::dmat4 tM = glm::translate( glm::dmat4( 1.0 ), glm::dvec3( translation.x, translation.y,
|
||||
translation.z ) );
|
||||
// center
|
||||
glm::dmat4 cM = glm::translate( glm::dmat4( 1.0 ), glm::dvec3( center.x, center.y, center.z ) );
|
||||
glm::dmat4 ncM = glm::translate( glm::dmat4( 1.0 ), glm::dvec3( -center.x, -center.y,
|
||||
-center.z ) );
|
||||
|
||||
// scale
|
||||
glm::dmat4 sM = glm::scale( glm::dmat4( 1.0 ), glm::dvec3( scale.x, scale.y, scale.z ) );
|
||||
|
||||
// scaleOrientation
|
||||
scale_axis.GetVector( rX, rY, rZ );
|
||||
glm::dmat4 srM = glm::rotate( glm::dmat4( 1.0 ), scale_angle, glm::dvec3( rX, rY, rZ ) );
|
||||
glm::dmat4 nsrM = glm::rotate( glm::dmat4( 1.0 ), -scale_angle, glm::dvec3( rX, rY, rZ ) );
|
||||
|
||||
// resultant point:
|
||||
// P' = T x C x R x SR x S x -SR x -C x P
|
||||
// resultant transform:
|
||||
// tx0 = tM * cM * rM * srM * sM * nsrM * ncM
|
||||
glm::dmat4 tx0;
|
||||
|
||||
if( nullptr != aTransform )
|
||||
tx0 = (*aTransform) * tM * cM * rM * srM * sM * nsrM * ncM;
|
||||
else
|
||||
tx0 = tM * cM * rM * srM * sM * nsrM * ncM;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// prepare all shapes
|
||||
do
|
||||
{
|
||||
std::vector< SGSHAPE* >::iterator sL = m_Shape.begin();
|
||||
std::vector< SGSHAPE* >::iterator eL = m_Shape.end();
|
||||
|
||||
while( sL != eL && ok )
|
||||
{
|
||||
ok = (*sL)->Prepare( &tx0, materials, meshes );
|
||||
++sL;
|
||||
}
|
||||
|
||||
sL = m_RShape.begin();
|
||||
eL = m_RShape.end();
|
||||
|
||||
while( sL != eL && ok )
|
||||
{
|
||||
ok = (*sL)->Prepare( &tx0, materials, meshes );
|
||||
++sL;
|
||||
}
|
||||
|
||||
} while( 0 );
|
||||
|
||||
// prepare all transforms
|
||||
do
|
||||
{
|
||||
std::vector< SCENEGRAPH* >::iterator sL = m_Transforms.begin();
|
||||
std::vector< SCENEGRAPH* >::iterator eL = m_Transforms.end();
|
||||
|
||||
while( sL != eL && ok )
|
||||
{
|
||||
ok = (*sL)->Prepare( &tx0, materials, meshes );
|
||||
++sL;
|
||||
}
|
||||
|
||||
sL = m_RTransforms.begin();
|
||||
eL = m_RTransforms.end();
|
||||
|
||||
while( sL != eL && ok )
|
||||
{
|
||||
ok = (*sL)->Prepare( &tx0, materials, meshes );
|
||||
++sL;
|
||||
}
|
||||
|
||||
} while( 0 );
|
||||
|
||||
return ok;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file scenegraph.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCENE_GRAPH_H
|
||||
#define SCENE_GRAPH_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
class SGSHAPE;
|
||||
|
||||
/**
|
||||
* Define the basic data set required to represent a 3D model.
|
||||
*
|
||||
* This model must remain compatible with VRML2.0 in order to facilitate VRML export of
|
||||
* scene graph data created by available 3D plugins.
|
||||
*/
|
||||
class SCENEGRAPH : public SGNODE
|
||||
{
|
||||
public:
|
||||
void unlinkChildNode( const SGNODE* aNode ) override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) override;
|
||||
|
||||
SCENEGRAPH( SGNODE* aParent );
|
||||
virtual ~SCENEGRAPH();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
SGNODE* FindNode(const char *aNodeName, const SGNODE *aCaller) override;
|
||||
bool AddRefNode( SGNODE* aNode ) override;
|
||||
bool AddChildNode( SGNODE* aNode ) override;
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
bool Prepare( const glm::dmat4* aTransform, S3D::MATLIST& materials,
|
||||
std::vector< SMESH >& meshes );
|
||||
|
||||
private:
|
||||
void unlinkNode( const SGNODE* aNode, bool isChild );
|
||||
bool addNode( SGNODE* aNode, bool isChild );
|
||||
|
||||
public:
|
||||
// note: order of transformation is Translate, Rotate, Offset
|
||||
SGPOINT center;
|
||||
SGPOINT translation;
|
||||
SGVECTOR rotation_axis;
|
||||
double rotation_angle; // radians
|
||||
SGPOINT scale;
|
||||
SGVECTOR scale_axis;
|
||||
double scale_angle; // radians
|
||||
|
||||
private:
|
||||
// The following are items which may be defined for reuse
|
||||
// in a VRML output file. They do not necessarily correspond
|
||||
// to the use of DEF within a VRML input file; it is the
|
||||
// responsibility of the plugin to perform any necessary
|
||||
// conversions to comply with the restrictions imposed by
|
||||
// this scene graph structure
|
||||
std::vector< SCENEGRAPH* > m_Transforms; // local Transform nodes
|
||||
std::vector< SGSHAPE* > m_Shape; // local Shape nodes
|
||||
|
||||
std::vector< SCENEGRAPH* > m_RTransforms; // referenced Transform nodes
|
||||
std::vector< SGSHAPE* > m_RShape; // referenced Shape nodes
|
||||
};
|
||||
|
||||
/*
|
||||
p.120
|
||||
Transform {
|
||||
center 0 0 0
|
||||
children []
|
||||
rotation 0 0 1 0
|
||||
scale 1 1 1
|
||||
scaleOrientation 0 0 1 0
|
||||
translation 0 0 0
|
||||
bboxCenter 0 0 0
|
||||
bboxSize -1 -1 -1
|
||||
}
|
||||
*/
|
||||
|
||||
#endif // SCENE_GRAPH_H
|
||||
@@ -1,363 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_appearance.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
|
||||
|
||||
SGAPPEARANCE::SGAPPEARANCE( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_APPEARANCE;
|
||||
|
||||
// defaults in accord with VRML2.0 spec
|
||||
ambient.SetColor( 0.05317f, 0.17879f, 0.01804f );
|
||||
shininess = 0.2f;
|
||||
transparency = 0.0f;
|
||||
diffuse.SetColor( 0.8f, 0.8f, 0.8f );
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_SHAPE != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] inappropriate parent to SGAPPEARANCE (type %s )" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_SHAPE == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGAPPEARANCE::~SGAPPEARANCE()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGSHAPE may be parent to a SGAPPEARANCE
|
||||
if( nullptr != aParent && S3D::SGTYPE_SHAPE != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetEmissive( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
return emissive.SetColor( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetEmissive( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK_MSG( aRGBColor, false, wxT( "NULL pointer passed for aRGBColor" ) );
|
||||
|
||||
return emissive.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetEmissive( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
return emissive.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetDiffuse( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
return diffuse.SetColor( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetDiffuse( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK_MSG( aRGBColor, false, wxT( "NULL pointer passed for aRGBColor" ) );
|
||||
|
||||
return diffuse.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetDiffuse( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
return diffuse.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetSpecular( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
return specular.SetColor( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetSpecular( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK_MSG( aRGBColor, false, wxT( "NULL pointer passed for aRGBColor" ) );
|
||||
|
||||
return specular.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
bool SGAPPEARANCE::SetSpecular( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
return specular.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetAmbient( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
return ambient.SetColor( aRVal, aGVal, aBVal );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetAmbient( const SGCOLOR* aRGBColor )
|
||||
{
|
||||
wxCHECK_MSG( aRGBColor, false, wxT( "NULL pointer passed for aRGBColor" ) );
|
||||
|
||||
return ambient.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::SetAmbient( const SGCOLOR& aRGBColor )
|
||||
{
|
||||
return ambient.SetColor( aRGBColor );
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGAPPEARANCE::FindNode( const char* aNodeName, const SGNODE* aCaller) noexcept
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void SGAPPEARANCE::unlinkChildNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK_MSG( aCaller, /* void */,
|
||||
wxT( "unexpected code branch; node should have no children or refs" ) );
|
||||
}
|
||||
|
||||
|
||||
void SGAPPEARANCE::unlinkRefNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK_MSG( aCaller, /* void */,
|
||||
wxT( "unexpected code branch; node should have no children or refs" ) );
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::AddRefNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK_MSG( aNode, false, wxT( "this node does not accept children or refs" ) );
|
||||
|
||||
// This is redundant but it keeps gcc from generating a warning on debug builds.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::AddChildNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK_MSG( aNode, false, wxT( "this node does not accept children or refs" ) );
|
||||
|
||||
// This is redundant but it keeps gcc from generating a warning on debug builds.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SGAPPEARANCE::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << " appearance DEF " << GetName() << " Appearance {\n";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " appearance USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " appearance Appearance {\n";
|
||||
}
|
||||
|
||||
aFile << " material Material {\n";
|
||||
|
||||
std::string tmp;
|
||||
float ambr, ambg, ambb;
|
||||
ambient.GetColor( ambr, ambg, ambb );
|
||||
float amb = ( 0.212671 * ambr + 0.71516 * ambg + 0.072169 * ambb );
|
||||
diffuse.GetColor( ambr, ambg, ambb );
|
||||
float den = ( 0.212671 * ambr + 0.71516 * ambg + 0.072169 * ambb );
|
||||
|
||||
if( den < 0.004f )
|
||||
den = 0.004f;
|
||||
|
||||
amb /= den;
|
||||
|
||||
if( amb > 1.0f )
|
||||
amb = 1.0f;
|
||||
|
||||
S3D::FormatFloat( tmp, amb );
|
||||
aFile << " ambientIntensity " << tmp << "\n";
|
||||
|
||||
float red, green, blue;
|
||||
diffuse.GetColor( red, green, blue );
|
||||
S3D::FormatFloat( tmp, red );
|
||||
aFile << " diffuseColor " << tmp << " ";
|
||||
S3D::FormatFloat( tmp, green );
|
||||
aFile << tmp << " ";
|
||||
S3D::FormatFloat( tmp, blue);
|
||||
aFile << tmp << "\n";
|
||||
|
||||
emissive.GetColor( red, green, blue );
|
||||
S3D::FormatFloat( tmp, red );
|
||||
aFile << " emissiveColor " << tmp << " ";
|
||||
S3D::FormatFloat( tmp, green );
|
||||
aFile << tmp << " ";
|
||||
S3D::FormatFloat( tmp, blue);
|
||||
aFile << tmp << "\n";
|
||||
|
||||
S3D::FormatFloat( tmp, shininess );
|
||||
aFile << " shininess " << tmp << "\n";
|
||||
|
||||
specular.GetColor( red, green, blue );
|
||||
S3D::FormatFloat( tmp, red );
|
||||
aFile << " specularColor " << tmp << " ";
|
||||
S3D::FormatFloat( tmp, green );
|
||||
aFile << tmp << " ";
|
||||
S3D::FormatFloat( tmp, blue);
|
||||
aFile << tmp << "\n";
|
||||
|
||||
S3D::FormatFloat( tmp, transparency );
|
||||
aFile << " transparency " << tmp << "\n";
|
||||
|
||||
aFile << "} }\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK_MSG( m_Parent, false, wxT( "corrupt data; m_aParent is NULL" ) );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK_MSG( parentNode == m_Parent, false, wxT( "corrupt data; parentNode != m_aParent" ) );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
S3D::WriteColor( aFile, ambient );
|
||||
aFile.write( (char*) &shininess, sizeof( shininess ) );
|
||||
aFile.write( (char*) &transparency, sizeof( transparency ) );
|
||||
S3D::WriteColor( aFile, diffuse );
|
||||
S3D::WriteColor( aFile, emissive );
|
||||
S3D::WriteColor( aFile, specular );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGAPPEARANCE::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
S3D::ReadColor( aFile, ambient );
|
||||
aFile.read( (char*) &shininess, sizeof( shininess ) );
|
||||
aFile.read( (char*) &transparency, sizeof( transparency ) );
|
||||
S3D::ReadColor( aFile, diffuse );
|
||||
S3D::ReadColor( aFile, emissive );
|
||||
S3D::ReadColor( aFile, specular );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_appearance.h
|
||||
*/
|
||||
|
||||
#ifndef SG_APPEARANCE_H
|
||||
#define SG_APPEARANCE_H
|
||||
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
/**
|
||||
* Defines the generic material appearance of a scenegraph object.
|
||||
*/
|
||||
class SGAPPEARANCE : public SGNODE
|
||||
{
|
||||
public:
|
||||
void unlinkChildNode( const SGNODE* aNode ) noexcept override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) noexcept override;
|
||||
|
||||
SGAPPEARANCE( SGNODE* aParent );
|
||||
virtual ~SGAPPEARANCE();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
bool SetEmissive( float aRVal, float aGVal, float aBVal );
|
||||
bool SetEmissive( const SGCOLOR* aRGBColor );
|
||||
bool SetEmissive( const SGCOLOR& aRGBColor );
|
||||
|
||||
bool SetDiffuse( float aRVal, float aGVal, float aBVal );
|
||||
bool SetDiffuse( const SGCOLOR* aRGBColor );
|
||||
bool SetDiffuse( const SGCOLOR& aRGBColor );
|
||||
|
||||
bool SetSpecular( float aRVal, float aGVal, float aBVal );
|
||||
bool SetSpecular( const SGCOLOR* aRGBColor );
|
||||
bool SetSpecular( const SGCOLOR& aRGBColor );
|
||||
|
||||
bool SetAmbient( float aRVal, float aGVal, float aBVal );
|
||||
bool SetAmbient( const SGCOLOR* aRGBColor );
|
||||
bool SetAmbient( const SGCOLOR& aRGBColor );
|
||||
|
||||
SGNODE* FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override;
|
||||
bool AddRefNode( SGNODE* aNode ) noexcept override;
|
||||
bool AddChildNode( SGNODE* aNode ) noexcept override;
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
float shininess; // default 0.2
|
||||
float transparency; // default 0.0
|
||||
SGCOLOR ambient; // default 0.05317 0.17879 0.01804
|
||||
SGCOLOR diffuse; // default 0.8 0.8 0.8
|
||||
SGCOLOR emissive; // default 0.0 0.0 0.0
|
||||
SGCOLOR specular; // default 0.0 0.0 0.0
|
||||
|
||||
};
|
||||
|
||||
#endif // SG_APPEARANCE_H
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "plugins/3dapi/sg_base.h"
|
||||
|
||||
|
||||
SGCOLOR::SGCOLOR()
|
||||
{
|
||||
red = 0.0;
|
||||
green = 0.0;
|
||||
blue = 0.0;
|
||||
}
|
||||
|
||||
SGCOLOR::SGCOLOR( float aRVal, float aGVal, float aBVal )
|
||||
{
|
||||
if( !checkRange( aRVal, aGVal, aBVal ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid value passed to constructor" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
red = 0.0;
|
||||
green = 0.0;
|
||||
blue = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
red = aRVal;
|
||||
green = aGVal;
|
||||
blue = aBVal;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLOR::GetColor( float& aRedVal, float& aGreenVal, float& aBlueVal ) const noexcept
|
||||
{
|
||||
aRedVal = red;
|
||||
aGreenVal = green;
|
||||
aBlueVal = blue;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLOR::GetColor( SGCOLOR& aColor ) const noexcept
|
||||
{
|
||||
aColor.red = red;
|
||||
aColor.green = green;
|
||||
aColor.blue = blue;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLOR::GetColor( SGCOLOR* aColor ) const noexcept
|
||||
{
|
||||
wxCHECK_MSG( aColor, /* void */, wxT( "NULL pointer passed for aRGBColor" ) );
|
||||
|
||||
aColor->red = red;
|
||||
aColor->green = green;
|
||||
aColor->blue = blue;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLOR::SetColor( float aRedVal, float aGreenVal, float aBlueVal )
|
||||
{
|
||||
if( !checkRange( aRedVal, aGreenVal, aBlueVal ) )
|
||||
return false;
|
||||
|
||||
red = aRedVal;
|
||||
green = aGreenVal;
|
||||
blue = aBlueVal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLOR::SetColor( const SGCOLOR& aColor ) noexcept
|
||||
{
|
||||
red = aColor.red;
|
||||
green = aColor.green;
|
||||
blue = aColor.blue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLOR::SetColor( const SGCOLOR* aColor ) noexcept
|
||||
{
|
||||
wxCHECK_MSG( aColor, false, wxT( "NULL pointer passed for aRGBColor" ) );
|
||||
|
||||
red = aColor->red;
|
||||
green = aColor->green;
|
||||
blue = aColor->blue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLOR::checkRange( float aRedVal, float aGreenVal, float aBlueVal ) const noexcept
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
if( aRedVal < 0.0 || aRedVal > 1.0 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid RED value: %g" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aRedVal );
|
||||
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if( aGreenVal < 0.0 || aGreenVal > 1.0 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid GREEN value: %g" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aGreenVal );
|
||||
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if( aBlueVal < 0.0 || aBlueVal > 1.0 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] invalid BLUE value: %g" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aBlueVal );
|
||||
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
SGPOINT::SGPOINT()
|
||||
{
|
||||
x = 0.0;
|
||||
y = 0.0;
|
||||
z = 0.0;
|
||||
}
|
||||
|
||||
|
||||
SGPOINT::SGPOINT( double aXVal, double aYVal, double aZVal ) noexcept
|
||||
{
|
||||
x = aXVal;
|
||||
y = aYVal;
|
||||
z = aZVal;
|
||||
}
|
||||
|
||||
|
||||
void SGPOINT::GetPoint( const double& aXVal, const double& aYVal, const double& aZVal ) noexcept
|
||||
{
|
||||
x = aXVal;
|
||||
y = aYVal;
|
||||
z = aZVal;
|
||||
}
|
||||
|
||||
|
||||
void SGPOINT::GetPoint( const SGPOINT& aPoint ) noexcept
|
||||
{
|
||||
x = aPoint.x;
|
||||
y = aPoint.y;
|
||||
z = aPoint.z;
|
||||
}
|
||||
|
||||
|
||||
void SGPOINT::GetPoint( const SGPOINT* aPoint ) noexcept
|
||||
{
|
||||
wxCHECK_MSG( aPoint, /* void */, wxT( "NULL pointer passed for aPoint" ) );
|
||||
|
||||
x = aPoint->x;
|
||||
y = aPoint->y;
|
||||
z = aPoint->z;
|
||||
}
|
||||
|
||||
|
||||
void SGPOINT::SetPoint( double aXVal, double aYVal, double aZVal ) noexcept
|
||||
{
|
||||
x = aXVal;
|
||||
y = aYVal;
|
||||
z = aZVal;
|
||||
}
|
||||
|
||||
|
||||
void SGPOINT::SetPoint( const SGPOINT& aPoint ) noexcept
|
||||
{
|
||||
x = aPoint.x;
|
||||
y = aPoint.y;
|
||||
z = aPoint.z;
|
||||
}
|
||||
|
||||
|
||||
SGVECTOR::SGVECTOR()
|
||||
{
|
||||
vx = 0.0;
|
||||
vy = 0.0;
|
||||
vz = 1.0;
|
||||
}
|
||||
|
||||
|
||||
SGVECTOR::SGVECTOR( double aXVal, double aYVal, double aZVal )
|
||||
{
|
||||
vx = aXVal;
|
||||
vy = aYVal;
|
||||
vz = aZVal;
|
||||
normalize();
|
||||
}
|
||||
|
||||
|
||||
void SGVECTOR::GetVector( double& aXVal, double& aYVal, double& aZVal ) const noexcept
|
||||
{
|
||||
aXVal = vx;
|
||||
aYVal = vy;
|
||||
aZVal = vz;
|
||||
}
|
||||
|
||||
|
||||
void SGVECTOR::SetVector( double aXVal, double aYVal, double aZVal )
|
||||
{
|
||||
vx = aXVal;
|
||||
vy = aYVal;
|
||||
vz = aZVal;
|
||||
normalize();
|
||||
}
|
||||
|
||||
|
||||
void SGVECTOR::SetVector( const SGVECTOR& aVector )
|
||||
{
|
||||
aVector.GetVector( vx, vy, vz );
|
||||
}
|
||||
|
||||
|
||||
void SGVECTOR::normalize( void ) noexcept
|
||||
{
|
||||
double dx = vx * vx;
|
||||
double dy = vy * vy;
|
||||
double dz = vz * vz;
|
||||
double dv2 = sqrt( dx + dy + dz );
|
||||
|
||||
if( ( dx + dy + dz ) < 1e-8 )
|
||||
{
|
||||
// use the default; the numbers are too small to be believable
|
||||
vx = 0.0;
|
||||
vy = 0.0;
|
||||
vz = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
vx /= dv2;
|
||||
vy /= dv2;
|
||||
vz /= dv2;
|
||||
}
|
||||
|
||||
|
||||
SGVECTOR& SGVECTOR::operator=( const SGVECTOR& source ) noexcept
|
||||
{
|
||||
vx = source.vx;
|
||||
vy = source.vy;
|
||||
vz = source.vz;
|
||||
return *this;
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_colors.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
|
||||
|
||||
SGCOLORS::SGCOLORS( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_COLORS;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] inappropriate parent to SGCOLORS (type %s)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_FACESET == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGCOLORS::~SGCOLORS()
|
||||
{
|
||||
colors.clear();
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGFACESET may be parent to a SGCOLORS
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGCOLORS::FindNode(const char* aNodeName, const SGNODE *aCaller) noexcept
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLORS::unlinkChildNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK( aCaller, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
void SGCOLORS::unlinkRefNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK( aCaller, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::AddRefNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::AddChildNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::GetColorList( size_t& aListSize, SGCOLOR*& aColorList )
|
||||
{
|
||||
if( colors.empty() )
|
||||
{
|
||||
aListSize = 0;
|
||||
aColorList = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
aListSize = colors.size();
|
||||
aColorList = &colors[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLORS::SetColorList( size_t aListSize, const SGCOLOR* aColorList )
|
||||
{
|
||||
colors.clear();
|
||||
|
||||
if( 0 == aListSize || nullptr == aColorList )
|
||||
return;
|
||||
|
||||
for( size_t i = 0; i < aListSize; ++i )
|
||||
colors.push_back( aColorList[i] );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLORS::AddColor( double aRedValue, double aGreenValue, double aBlueValue )
|
||||
{
|
||||
colors.emplace_back( aRedValue, aGreenValue, aBlueValue );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLORS::AddColor( const SGCOLOR& aColor )
|
||||
{
|
||||
colors.push_back( aColor );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void SGCOLORS::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( colors.empty() )
|
||||
return false;
|
||||
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << "color DEF " << GetName() << " Color { color [\n ";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << "color USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << "color Color { color [\n ";
|
||||
}
|
||||
|
||||
std::string tmp;
|
||||
size_t n = colors.size();
|
||||
bool nline = false;
|
||||
|
||||
for( size_t i = 0; i < n; )
|
||||
{
|
||||
S3D::FormatColor( tmp, colors[i] );
|
||||
float r,g,b;
|
||||
colors[i].GetColor(r, g, b);
|
||||
aFile << tmp ;
|
||||
++i;
|
||||
|
||||
if( i < n )
|
||||
{
|
||||
aFile << ",";
|
||||
|
||||
if( nline )
|
||||
{
|
||||
aFile << "\n ";
|
||||
nline = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nline = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
aFile << "] }\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK( m_Parent, false );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
size_t ncolors = colors.size();
|
||||
aFile.write( (char*)&ncolors, sizeof(size_t) );
|
||||
|
||||
for( size_t i = 0; i < ncolors; ++i )
|
||||
S3D::WriteColor( aFile, colors[i] );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOLORS::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
wxCHECK( colors.empty(), false );
|
||||
|
||||
size_t ncolors;
|
||||
aFile.read( (char*) &ncolors, sizeof( size_t ) );
|
||||
SGCOLOR tmp;
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
for( size_t i = 0; i < ncolors; ++i )
|
||||
{
|
||||
if( !S3D::ReadColor( aFile, tmp ) || aFile.fail() )
|
||||
return false;
|
||||
|
||||
colors.push_back( tmp );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_colors.h
|
||||
*/
|
||||
|
||||
#ifndef SG_COLORS_H
|
||||
#define SG_COLORS_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
/**
|
||||
* Define an RGB color set for a scenegraph object.
|
||||
*/
|
||||
class SGCOLORS : public SGNODE
|
||||
{
|
||||
public:
|
||||
SGCOLORS( SGNODE* aParent );
|
||||
virtual ~SGCOLORS();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
void unlinkChildNode( const SGNODE* aNode ) noexcept override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) noexcept override;
|
||||
|
||||
SGNODE* FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override;
|
||||
bool AddRefNode( SGNODE* aNode ) noexcept override;
|
||||
bool AddChildNode( SGNODE* aNode ) noexcept override;
|
||||
|
||||
bool GetColorList( size_t& aListSize, SGCOLOR*& aColorList );
|
||||
void SetColorList( size_t aListSize, const SGCOLOR* aColorList );
|
||||
void AddColor( double aRedValue, double aGreenValue, double aBlueValue );
|
||||
void AddColor( const SGCOLOR& aColor );
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
std::vector< SGCOLOR > colors;
|
||||
};
|
||||
|
||||
#endif // SG_COLORS_H
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "3d_cache/sg/sg_coordindex.h"
|
||||
|
||||
|
||||
SGCOORDINDEX::SGCOORDINDEX( SGNODE* aParent ) : SGINDEX( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_COORDINDEX;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGCOORDINDEX::~SGCOORDINDEX()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDINDEX::GatherCoordIndices( std::vector< int >& aIndexList )
|
||||
{
|
||||
if( index.empty() )
|
||||
return;
|
||||
|
||||
aIndexList.insert( aIndexList.end(), index.begin(), index.end() );
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_coordindex.h
|
||||
*/
|
||||
|
||||
#ifndef SG_COORDINDEX_H
|
||||
#define SG_COORDINDEX_H
|
||||
|
||||
#include "3d_cache/sg/sg_index.h"
|
||||
|
||||
/**
|
||||
* An object to maintain a coordinate index list.
|
||||
*
|
||||
* Users must ensure that coordinate indices are specified as triplets (triangular faces)
|
||||
* since no checking is performed. In instances where it is not possible to determine which
|
||||
* side of the triangle is to be rendered (for example IGES entities) then the user must
|
||||
* supply each triplet in both point orders.
|
||||
*/
|
||||
class SGCOORDINDEX : public SGINDEX
|
||||
{
|
||||
public:
|
||||
SGCOORDINDEX( SGNODE* aParent );
|
||||
virtual ~SGCOORDINDEX();
|
||||
|
||||
/**
|
||||
* Add all coordinate indices to the given list in preparation for a normals calculation.
|
||||
*/
|
||||
void GatherCoordIndices( std::vector< int >& aIndexList );
|
||||
};
|
||||
|
||||
#endif // SG_COORDINDEX_H
|
||||
@@ -1,363 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_coords.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
#include "3d_cache/sg/sg_normals.h"
|
||||
#include "3d_cache/sg/sg_faceset.h"
|
||||
|
||||
|
||||
SGCOORDS::SGCOORDS( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_COORDS;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] inappropriate parent to SGCOORDS (type %s)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_FACESET == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGCOORDS::~SGCOORDS()
|
||||
{
|
||||
coords.clear();
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGFACESET may be parent to a SGCOORDS
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGCOORDS::FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDS::unlinkChildNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK( aCaller, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDS::unlinkRefNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK( aCaller, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::AddRefNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::AddChildNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::GetCoordsList( size_t& aListSize, SGPOINT*& aCoordsList )
|
||||
{
|
||||
if( coords.empty() )
|
||||
{
|
||||
aListSize = 0;
|
||||
aCoordsList = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
aListSize = coords.size();
|
||||
aCoordsList = &coords[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDS::SetCoordsList( size_t aListSize, const SGPOINT* aCoordsList )
|
||||
{
|
||||
coords.clear();
|
||||
|
||||
if( 0 == aListSize || nullptr == aCoordsList )
|
||||
return;
|
||||
|
||||
for( size_t i = 0; i < aListSize; ++i )
|
||||
coords.push_back( aCoordsList[i] );
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDS::AddCoord( double aXValue, double aYValue, double aZValue )
|
||||
{
|
||||
coords.emplace_back( aXValue, aYValue, aZValue );
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDS::AddCoord( const SGPOINT& aPoint )
|
||||
{
|
||||
coords.push_back( aPoint );
|
||||
}
|
||||
|
||||
|
||||
void SGCOORDS::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( coords.empty() )
|
||||
return false;
|
||||
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << " coord DEF " << GetName() << " Coordinate { point [\n ";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " coord USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " coord Coordinate { point [\n ";
|
||||
}
|
||||
|
||||
std::string tmp;
|
||||
size_t n = coords.size();
|
||||
bool nline = false;
|
||||
SGPOINT pt;
|
||||
|
||||
for( size_t i = 0; i < n; )
|
||||
{
|
||||
// ensure VRML output has 1U = 0.1 inch as per legacy kicad expectations
|
||||
pt = coords[i];
|
||||
pt.x /= 2.54;
|
||||
pt.y /= 2.54;
|
||||
pt.z /= 2.54;
|
||||
S3D::FormatPoint( tmp, pt );
|
||||
aFile << tmp ;
|
||||
++i;
|
||||
|
||||
if( i < n )
|
||||
{
|
||||
aFile << ",";
|
||||
|
||||
if( nline )
|
||||
{
|
||||
aFile << "\n ";
|
||||
nline = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nline = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
aFile << "] }\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK( m_Parent, false );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
size_t npts = coords.size();
|
||||
aFile.write( (char*)&npts, sizeof(size_t) );
|
||||
|
||||
for( size_t i = 0; i < npts; ++i )
|
||||
S3D::WritePoint( aFile, coords[i] );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
wxCHECK( coords.empty(), false );
|
||||
|
||||
size_t npts;
|
||||
aFile.read( (char*) &npts, sizeof( size_t ) );
|
||||
SGPOINT tmp;
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
for( size_t i = 0; i < npts; ++i )
|
||||
{
|
||||
if( !S3D::ReadPoint( aFile, tmp ) || aFile.fail() )
|
||||
return false;
|
||||
|
||||
coords.push_back( tmp );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGCOORDS::CalcNormals( SGFACESET* callingNode, SGNODE** aPtr )
|
||||
{
|
||||
if( aPtr )
|
||||
*aPtr = nullptr;
|
||||
|
||||
if( nullptr == m_Parent || nullptr == callingNode )
|
||||
return false;
|
||||
|
||||
// the parent and all references must have indices; collect all
|
||||
// indices into one std::vector<>
|
||||
std::vector< int > ilist;
|
||||
SGNORMALS* np = nullptr;
|
||||
|
||||
if( callingNode == m_Parent )
|
||||
{
|
||||
((SGFACESET*)m_Parent)->GatherCoordIndices( ilist );
|
||||
|
||||
std::list< SGNODE* >::iterator sB = m_BackPointers.begin();
|
||||
std::list< SGNODE* >::iterator eB = m_BackPointers.end();
|
||||
|
||||
while( sB != eB )
|
||||
{
|
||||
SGFACESET* fp = (SGFACESET*)(*sB);
|
||||
fp->GatherCoordIndices( ilist );
|
||||
++sB;
|
||||
}
|
||||
|
||||
np = ( (SGFACESET*) m_Parent )->m_Normals;
|
||||
|
||||
if( !np )
|
||||
np = new SGNORMALS( m_Parent );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
callingNode->GatherCoordIndices( ilist );
|
||||
np = callingNode->m_Normals;
|
||||
|
||||
if( !np )
|
||||
np = new SGNORMALS( callingNode );
|
||||
|
||||
}
|
||||
|
||||
if( S3D::CalcTriangleNormals( coords, ilist, np->norms ) )
|
||||
{
|
||||
if( aPtr )
|
||||
*aPtr = np;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
delete np;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_coords.h
|
||||
*/
|
||||
|
||||
#ifndef SG_COORDS_H
|
||||
#define SG_COORDS_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
class SGFACESET;
|
||||
|
||||
/**
|
||||
* Define a vertex coordinate set for a scenegraph object.
|
||||
*/
|
||||
class SGCOORDS : public SGNODE
|
||||
{
|
||||
public:
|
||||
SGCOORDS( SGNODE* aParent );
|
||||
virtual ~SGCOORDS();
|
||||
|
||||
void unlinkChildNode( const SGNODE* aNode ) noexcept override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) noexcept override;
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
SGNODE* FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override;
|
||||
bool AddRefNode( SGNODE* aNode ) noexcept override;
|
||||
bool AddChildNode( SGNODE* aNode ) noexcept override;
|
||||
|
||||
bool GetCoordsList( size_t& aListSize, SGPOINT*& aCoordsList );
|
||||
void SetCoordsList( size_t aListSize, const SGPOINT* aCoordsList );
|
||||
void AddCoord( double aXValue, double aYValue, double aZValue );
|
||||
void AddCoord( const SGPOINT& aPoint );
|
||||
|
||||
/**
|
||||
* Calculate normals for this coordinate list and sets the normals list in the
|
||||
* parent #SGFACESET.
|
||||
*/
|
||||
bool CalcNormals( SGFACESET* callingNode, SGNODE** aPtr = nullptr );
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
std::vector< SGPOINT > coords;
|
||||
};
|
||||
|
||||
#endif // SG_COORDS_H
|
||||
@@ -1,992 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_faceset.h"
|
||||
#include "3d_cache/sg/sg_colors.h"
|
||||
#include "3d_cache/sg/sg_coords.h"
|
||||
#include "3d_cache/sg/sg_normals.h"
|
||||
#include "3d_cache/sg/sg_coordindex.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
|
||||
|
||||
SGFACESET::SGFACESET( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_FACESET;
|
||||
m_Colors = nullptr;
|
||||
m_Coords = nullptr;
|
||||
m_CoordIndices = nullptr;
|
||||
m_Normals = nullptr;
|
||||
m_RColors = nullptr;
|
||||
m_RCoords = nullptr;
|
||||
m_RNormals = nullptr;
|
||||
valid = false;
|
||||
validated = false;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_SHAPE != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] inappropriate parent to SGFACESET (type %s)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_SHAPE == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGFACESET::~SGFACESET()
|
||||
{
|
||||
// drop references
|
||||
if( m_RColors )
|
||||
{
|
||||
m_RColors->delNodeRef( this );
|
||||
m_RColors = nullptr;
|
||||
}
|
||||
|
||||
if( m_RCoords )
|
||||
{
|
||||
m_RCoords->delNodeRef( this );
|
||||
m_RCoords = nullptr;
|
||||
}
|
||||
|
||||
if( m_RNormals )
|
||||
{
|
||||
m_RNormals->delNodeRef( this );
|
||||
m_RNormals = nullptr;
|
||||
}
|
||||
|
||||
// delete owned objects
|
||||
if( m_Colors )
|
||||
{
|
||||
m_Colors->SetParent( nullptr, false );
|
||||
delete m_Colors;
|
||||
m_Colors = nullptr;
|
||||
}
|
||||
|
||||
if( m_Coords )
|
||||
{
|
||||
m_Coords->SetParent( nullptr, false );
|
||||
delete m_Coords;
|
||||
m_Coords = nullptr;
|
||||
}
|
||||
|
||||
if( m_Normals )
|
||||
{
|
||||
m_Normals->SetParent( nullptr, false );
|
||||
delete m_Normals;
|
||||
m_Normals = nullptr;
|
||||
}
|
||||
|
||||
if( m_CoordIndices )
|
||||
{
|
||||
m_CoordIndices->SetParent( nullptr, false );
|
||||
delete m_CoordIndices;
|
||||
m_CoordIndices = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGSHAPE may be parent to a SGFACESET
|
||||
if( nullptr != aParent && S3D::SGTYPE_SHAPE != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGFACESET::FindNode(const char *aNodeName, const SGNODE *aCaller)
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
SGNODE* np = nullptr;
|
||||
|
||||
if( m_Colors )
|
||||
{
|
||||
np = m_Colors->FindNode( aNodeName, this );
|
||||
|
||||
if( np )
|
||||
return np;
|
||||
}
|
||||
|
||||
if( m_Coords )
|
||||
{
|
||||
np = m_Coords->FindNode( aNodeName, this );
|
||||
|
||||
if( np )
|
||||
return np;
|
||||
}
|
||||
|
||||
if( m_CoordIndices )
|
||||
{
|
||||
np = m_CoordIndices->FindNode( aNodeName, this );
|
||||
|
||||
if( np )
|
||||
return np;
|
||||
}
|
||||
|
||||
if( m_Normals )
|
||||
{
|
||||
np = m_Normals->FindNode( aNodeName, this );
|
||||
|
||||
if( np )
|
||||
return np;
|
||||
}
|
||||
|
||||
// query the parent if appropriate
|
||||
if( aCaller == m_Parent || nullptr == m_Parent )
|
||||
return nullptr;
|
||||
|
||||
return m_Parent->FindNode( aNodeName, this );
|
||||
}
|
||||
|
||||
|
||||
void SGFACESET::unlinkNode( const SGNODE* aNode, bool isChild )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return;
|
||||
|
||||
valid = false;
|
||||
validated = false;
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
if( aNode == m_Colors )
|
||||
{
|
||||
m_Colors = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_Coords )
|
||||
{
|
||||
m_Coords = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_Normals )
|
||||
{
|
||||
m_Normals = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_CoordIndices )
|
||||
{
|
||||
m_CoordIndices = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aNode == m_RColors )
|
||||
{
|
||||
delNodeRef( this );
|
||||
m_RColors = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_RCoords )
|
||||
{
|
||||
delNodeRef( this );
|
||||
m_RCoords = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_RNormals )
|
||||
{
|
||||
delNodeRef( this );
|
||||
m_RNormals = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] unlinkNode() did not find its target" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
}
|
||||
|
||||
|
||||
void SGFACESET::unlinkChildNode( const SGNODE* aNode )
|
||||
{
|
||||
unlinkNode( aNode, true );
|
||||
}
|
||||
|
||||
|
||||
void SGFACESET::unlinkRefNode( const SGNODE* aNode )
|
||||
{
|
||||
unlinkNode( aNode, false );
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool SGFACESET::addNode( SGNODE* aNode, bool isChild )
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
valid = false;
|
||||
validated = false;
|
||||
|
||||
if( S3D::SGTYPE_COLORS == aNode->GetNodeType() )
|
||||
{
|
||||
if( m_Colors || m_RColors )
|
||||
{
|
||||
if( aNode != m_Colors && aNode != m_RColors )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] assigning multiple Colors nodes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
m_Colors = (SGCOLORS*)aNode;
|
||||
m_Colors->SetParent( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RColors = (SGCOLORS*)aNode;
|
||||
m_RColors->addNodeRef( this );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_COORDS == aNode->GetNodeType() )
|
||||
{
|
||||
if( m_Coords || m_RCoords )
|
||||
{
|
||||
if( aNode != m_Coords && aNode != m_RCoords )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] assigning multiple Colors nodes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
m_Coords = (SGCOORDS*)aNode;
|
||||
m_Coords->SetParent( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RCoords = (SGCOORDS*)aNode;
|
||||
m_RCoords->addNodeRef( this );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_NORMALS == aNode->GetNodeType() )
|
||||
{
|
||||
if( m_Normals || m_RNormals )
|
||||
{
|
||||
if( aNode != m_Normals && aNode != m_RNormals )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] assigning multiple Normals nodes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
m_Normals = (SGNORMALS*)aNode;
|
||||
m_Normals->SetParent( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RNormals = (SGNORMALS*)aNode;
|
||||
m_RNormals->addNodeRef( this );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_COORDINDEX == aNode->GetNodeType() )
|
||||
{
|
||||
if( m_CoordIndices )
|
||||
{
|
||||
if( aNode != m_CoordIndices )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG,
|
||||
wxT( "%s:%s:%d * [BUG] assigning multiple CoordIndex nodes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
m_CoordIndices = (SGCOORDINDEX*)aNode;
|
||||
m_CoordIndices->SetParent( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] object type '%s' is not a valid type for "
|
||||
"this object '%d'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aNode->GetName(),
|
||||
aNode->GetNodeType() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::AddRefNode( SGNODE* aNode )
|
||||
{
|
||||
return addNode( aNode, false );
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::AddChildNode( SGNODE* aNode )
|
||||
{
|
||||
return addNode( aNode, true );
|
||||
}
|
||||
|
||||
|
||||
void SGFACESET::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
|
||||
// rename all Colors and Indices
|
||||
if( m_Colors )
|
||||
m_Colors->ReNameNodes();
|
||||
|
||||
// rename all Coordinates and Indices
|
||||
if( m_Coords )
|
||||
m_Coords->ReNameNodes();
|
||||
|
||||
if( m_CoordIndices )
|
||||
m_CoordIndices->ReNameNodes();
|
||||
|
||||
// rename all Normals and Indices
|
||||
if( m_Normals )
|
||||
m_Normals->ReNameNodes();
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( ( nullptr == m_Coords && nullptr == m_RCoords ) || ( nullptr == m_CoordIndices ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << " geometry DEF " << GetName() << " IndexedFaceSet {\n";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << "USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " geometry IndexedFaceSet {\n";
|
||||
}
|
||||
|
||||
if( m_Coords )
|
||||
m_Coords->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_RCoords )
|
||||
m_RCoords->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_CoordIndices )
|
||||
m_CoordIndices->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_Normals || m_RNormals )
|
||||
aFile << " normalPerVertex TRUE\n";
|
||||
|
||||
if( m_Normals )
|
||||
m_Normals->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_RNormals )
|
||||
m_RNormals->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_Colors )
|
||||
m_Colors->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_RColors )
|
||||
m_RColors->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
aFile << "}\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK( m_Parent, false );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if any references are unwritten and swap parents if so
|
||||
if( nullptr != m_RCoords && !m_RCoords->isWritten() )
|
||||
m_RCoords->SwapParent( this );
|
||||
|
||||
if( nullptr != m_RNormals && !m_RNormals->isWritten() )
|
||||
m_RNormals->SwapParent( this );
|
||||
|
||||
if( nullptr != m_RColors && !m_RColors->isWritten() )
|
||||
m_RColors->SwapParent( this );
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
#define NITEMS 7
|
||||
bool items[NITEMS];
|
||||
int i;
|
||||
|
||||
for( i = 0; i < NITEMS; ++i )
|
||||
items[i] = 0;
|
||||
|
||||
i = 0;
|
||||
if( nullptr != m_Coords )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
if( nullptr != m_RCoords )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
if( nullptr != m_CoordIndices )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
if( nullptr != m_Normals )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
if( nullptr != m_RNormals )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
if( nullptr != m_Colors )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
if( nullptr != m_RColors )
|
||||
items[i] = true;
|
||||
|
||||
for( int jj = 0; jj < NITEMS; ++jj )
|
||||
aFile.write( (char*) &items[jj], sizeof( bool ) );
|
||||
|
||||
if( items[0] )
|
||||
m_Coords->WriteCache( aFile, this );
|
||||
|
||||
if( items[1] )
|
||||
aFile << "[" << m_RCoords->GetName() << "]";
|
||||
|
||||
if( items[2] )
|
||||
m_CoordIndices->WriteCache( aFile, this );
|
||||
|
||||
if( items[3] )
|
||||
m_Normals->WriteCache( aFile, this );
|
||||
|
||||
if( items[4] )
|
||||
aFile << "[" << m_RNormals->GetName() << "]";
|
||||
|
||||
if( items[5] )
|
||||
m_Colors->WriteCache( aFile, this );
|
||||
|
||||
if( items[6] )
|
||||
aFile << "[" << m_RColors->GetName() << "]";
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( m_Coords || m_RCoords || m_CoordIndices || m_Colors || m_RColors || m_Normals
|
||||
|| m_RNormals )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] non-empty node" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define NITEMS 7
|
||||
bool items[NITEMS];
|
||||
|
||||
for( int i = 0; i < NITEMS; ++i )
|
||||
aFile.read( (char*) &items[i], sizeof( bool ) );
|
||||
|
||||
if( ( items[0] && items[1] ) || ( items[3] && items[4] ) || ( items[5] && items[6] ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; multiple item definitions "
|
||||
"at position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
|
||||
if( items[0] )
|
||||
{
|
||||
if( S3D::SGTYPE_COORDS != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child coords tag at "
|
||||
"position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Coords = new SGCOORDS( this );
|
||||
m_Coords->SetName( name.c_str() );
|
||||
|
||||
if( !m_Coords->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; corrupt data while "
|
||||
"reading coords '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( items[1] )
|
||||
{
|
||||
if( S3D::SGTYPE_COORDS != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref coords tag at "
|
||||
"position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* np = FindNode( name.c_str(), this );
|
||||
|
||||
if( !np )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; cannot find ref "
|
||||
"coords '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_COORDS != np->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; type is not SGCOORDS "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RCoords = (SGCOORDS*)np;
|
||||
m_RCoords->addNodeRef( this );
|
||||
}
|
||||
|
||||
if( items[2] )
|
||||
{
|
||||
if( S3D::SGTYPE_COORDINDEX != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad coord index tag at "
|
||||
"position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_CoordIndices = new SGCOORDINDEX( this );
|
||||
m_CoordIndices->SetName( name.c_str() );
|
||||
|
||||
if( !m_CoordIndices->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data while reading coord "
|
||||
"index '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( items[3] )
|
||||
{
|
||||
if( S3D::SGTYPE_NORMALS != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child normals tag "
|
||||
"at position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Normals = new SGNORMALS( this );
|
||||
m_Normals->SetName( name.c_str() );
|
||||
|
||||
if( !m_Normals->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data while reading normals "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( items[4] )
|
||||
{
|
||||
if( S3D::SGTYPE_NORMALS != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref normals tag at "
|
||||
"position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* np = FindNode( name.c_str(), this );
|
||||
|
||||
if( !np )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt: cannot find ref normals "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_NORMALS != np->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt: type is not SGNORMALS '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RNormals = (SGNORMALS*)np;
|
||||
m_RNormals->addNodeRef( this );
|
||||
}
|
||||
|
||||
if( items[5] )
|
||||
{
|
||||
if( S3D::SGTYPE_COLORS != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child colors tag "
|
||||
"at position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Colors = new SGCOLORS( this );
|
||||
m_Colors->SetName( name.c_str() );
|
||||
|
||||
if( !m_Colors->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data while reading colors "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( items[6] )
|
||||
{
|
||||
if( S3D::SGTYPE_COLORS != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref colors tag at "
|
||||
"position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* np = FindNode( name.c_str(), this );
|
||||
|
||||
if( !np )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: cannot find ref colors "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_COLORS != np->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: type is not SGCOLORS "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RColors = (SGCOLORS*)np;
|
||||
m_RColors->addNodeRef( this );
|
||||
}
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::validate( void )
|
||||
{
|
||||
// verify the integrity of this object's data
|
||||
if( validated )
|
||||
return valid;
|
||||
|
||||
// ensure we have at least coordinates and their normals
|
||||
if( ( nullptr == m_Coords && nullptr == m_RCoords )
|
||||
|| ( nullptr == m_Normals && nullptr == m_RNormals )
|
||||
|| ( nullptr == m_CoordIndices ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; no vertices, vertex indices, "
|
||||
"or normals" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
validated = true;
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that there are >3 vertices
|
||||
SGCOORDS* coords = m_Coords;
|
||||
|
||||
if( nullptr == coords )
|
||||
coords = m_RCoords;
|
||||
|
||||
size_t nCoords = 0;
|
||||
SGPOINT* lCoords = nullptr;
|
||||
coords->GetCoordsList( nCoords, lCoords );
|
||||
|
||||
if( nCoords < 3 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; fewer than 3 vertices" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
validated = true;
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that nVertices is divisible by 3 (facets are triangles)
|
||||
size_t nCIdx = 0;
|
||||
int* lCIdx = nullptr;
|
||||
m_CoordIndices->GetIndices( nCIdx, lCIdx );
|
||||
|
||||
if( nCIdx < 3 || ( nCIdx % 3 > 0 ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; no vertex indices or not "
|
||||
"multiple of 3" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
validated = true;
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that vertex[n] >= 0 and < nVertices
|
||||
for( size_t i = 0; i < nCIdx; ++i )
|
||||
{
|
||||
if( lCIdx[i] < 0 || lCIdx[i] >= (int)nCoords )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; vertex index out of "
|
||||
"bounds" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
validated = true;
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check that there are as many normals as vertices
|
||||
size_t nNorms = 0;
|
||||
SGVECTOR* lNorms = nullptr;
|
||||
SGNORMALS* pNorms = m_Normals;
|
||||
|
||||
if( nullptr == pNorms )
|
||||
pNorms = m_RNormals;
|
||||
|
||||
pNorms->GetNormalList( nNorms, lNorms );
|
||||
|
||||
if( nNorms != nCoords )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; number of normals (%ul) does "
|
||||
"not match number of vertices (%ul)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( nNorms ),
|
||||
static_cast<unsigned long>( nCoords ) );
|
||||
|
||||
validated = true;
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there are colors then ensure there are as many colors as vertices
|
||||
SGCOLORS* pColors = m_Colors;
|
||||
|
||||
if( nullptr == pColors )
|
||||
pColors = m_RColors;
|
||||
|
||||
if( nullptr != pColors )
|
||||
{
|
||||
// we must have at least as many colors as vertices
|
||||
size_t nColor = 0;
|
||||
SGCOLOR* pColor = nullptr;
|
||||
pColors->GetColorList( nColor, pColor );
|
||||
}
|
||||
|
||||
validated = true;
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SGFACESET::GatherCoordIndices( std::vector< int >& aIndexList )
|
||||
{
|
||||
if( m_CoordIndices )
|
||||
m_CoordIndices->GatherCoordIndices( aIndexList );
|
||||
}
|
||||
|
||||
|
||||
bool SGFACESET::CalcNormals( SGNODE** aPtr )
|
||||
{
|
||||
SGCOORDS* coords = m_Coords;
|
||||
|
||||
if( m_RCoords )
|
||||
coords = m_RCoords;
|
||||
|
||||
if( nullptr == coords || coords->coords.empty() )
|
||||
return false;
|
||||
|
||||
if( m_Normals && !m_Normals->norms.empty( ) )
|
||||
return true;
|
||||
|
||||
if( m_RNormals && !m_RNormals->norms.empty( ) )
|
||||
return true;
|
||||
|
||||
return coords->CalcNormals( this, aPtr );
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_faceset.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SG_FACESET_H
|
||||
#define SG_FACESET_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
|
||||
class SGCOLORS;
|
||||
class SGCOORDS;
|
||||
class SGNORMALS;
|
||||
class SGCOLORINDEX;
|
||||
class SGCOORDINDEX;
|
||||
|
||||
/**
|
||||
* Define an indexed face set for a scenegraph.
|
||||
*/
|
||||
class SGFACESET : public SGNODE
|
||||
{
|
||||
public:
|
||||
SGFACESET( SGNODE* aParent );
|
||||
virtual ~SGFACESET();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
SGNODE* FindNode( const char* aNodeName, const SGNODE* aCaller ) override;
|
||||
bool AddRefNode( SGNODE* aNode ) override;
|
||||
bool AddChildNode( SGNODE* aNode ) override;
|
||||
|
||||
bool CalcNormals( SGNODE** aPtr );
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
/**
|
||||
* Add all internal coordinate indices to the given list in preparation for a normals
|
||||
* calculation.
|
||||
*/
|
||||
void GatherCoordIndices( std::vector< int >& aIndexList );
|
||||
|
||||
void unlinkChildNode( const SGNODE* aNode ) override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) override;
|
||||
|
||||
// validate the data held by this face set
|
||||
bool validate( void );
|
||||
|
||||
// owned objects
|
||||
SGCOLORS* m_Colors;
|
||||
SGCOORDS* m_Coords;
|
||||
SGCOORDINDEX* m_CoordIndices;
|
||||
SGNORMALS* m_Normals;
|
||||
|
||||
// referenced objects
|
||||
SGCOLORS* m_RColors;
|
||||
SGCOORDS* m_RCoords;
|
||||
SGNORMALS* m_RNormals;
|
||||
|
||||
private:
|
||||
bool valid;
|
||||
bool validated;
|
||||
void unlinkNode( const SGNODE* aNode, bool isChild );
|
||||
bool addNode( SGNODE* aNode, bool isChild );
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
p.88
|
||||
IndexedFaceSet {
|
||||
color NULL
|
||||
coord NULL
|
||||
normal NULL
|
||||
texCoord NULL
|
||||
ccw TRUE
|
||||
colorIndex []
|
||||
colorPerVertex TRUE
|
||||
convex TRUE
|
||||
coordIndex []
|
||||
creaseAngle 0
|
||||
normalIndex []
|
||||
normalPerVertex TRUE
|
||||
solid TRUE
|
||||
texCoordIndex []
|
||||
}
|
||||
*/
|
||||
|
||||
#endif // SG_FACESET_H
|
||||
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
|
||||
void S3D::FormatFloat( std::string& result, double value )
|
||||
{
|
||||
if( value < 1e-8 && value > -1e-8 )
|
||||
{
|
||||
result = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
// note: many VRML implementations use float so we use the max.
|
||||
// precision here of 8 digits.
|
||||
std::ostringstream out;
|
||||
out << std::setprecision( 8 ) << value;
|
||||
|
||||
result = out.str();
|
||||
|
||||
size_t p = result.find( '.' );
|
||||
|
||||
// trim trailing 0 if appropriate
|
||||
if( std::string::npos == p )
|
||||
return;
|
||||
|
||||
p = result.find_first_of( "eE" );
|
||||
|
||||
if( std::string::npos == p )
|
||||
{
|
||||
while( '0' == *( result.rbegin() ) )
|
||||
result.erase( result.size() - 1 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( '0' != result.at( p - 1 ) )
|
||||
return;
|
||||
|
||||
// trim all 0 to the left of 'p'
|
||||
std::string tmp = result.substr( p );
|
||||
result = result.substr( 0, p );
|
||||
|
||||
while( '0' == *( result.rbegin() ) )
|
||||
result.erase( result.size() - 1 );
|
||||
|
||||
result.append( tmp );
|
||||
}
|
||||
|
||||
|
||||
void S3D::FormatOrientation( std::string& result, const SGVECTOR& axis, double rotation )
|
||||
{
|
||||
double aX;
|
||||
double aY;
|
||||
double aZ;
|
||||
|
||||
axis.GetVector( aX, aY, aZ );
|
||||
FormatFloat( result, aX );
|
||||
std::string tmp;
|
||||
FormatFloat( tmp, aY );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
FormatFloat( tmp, aZ );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
FormatFloat( tmp, rotation );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
}
|
||||
|
||||
|
||||
void S3D::FormatPoint( std::string& result, const SGPOINT& point )
|
||||
{
|
||||
FormatFloat( result, point.x );
|
||||
|
||||
std::string tmp;
|
||||
FormatFloat( tmp, point.y );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
|
||||
FormatFloat( tmp, point.z );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
}
|
||||
|
||||
|
||||
void S3D::FormatVector( std::string& result, const SGVECTOR& aVector )
|
||||
{
|
||||
double X, Y, Z;
|
||||
aVector.GetVector( X, Y, Z );
|
||||
FormatFloat( result, X );
|
||||
|
||||
std::string tmp;
|
||||
FormatFloat( tmp, Y );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
|
||||
FormatFloat( tmp, Z );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
}
|
||||
|
||||
|
||||
void S3D::FormatColor( std::string& result, const SGCOLOR& aColor )
|
||||
{
|
||||
float R, G, B;
|
||||
aColor.GetColor( R, G, B );
|
||||
FormatFloat( result, R );
|
||||
|
||||
std::string tmp;
|
||||
FormatFloat( tmp, G );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
|
||||
FormatFloat( tmp, B );
|
||||
result.append( " " );
|
||||
result.append( tmp );
|
||||
}
|
||||
|
||||
|
||||
bool S3D::WritePoint( std::ostream& aFile, const SGPOINT& aPoint )
|
||||
{
|
||||
aFile.write( (char*) &aPoint.x, sizeof( aPoint.x ) );
|
||||
aFile.write( (char*) &aPoint.y, sizeof( aPoint.y ) );
|
||||
aFile.write( (char*) &aPoint.z, sizeof( aPoint.z ) );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::WriteVector( std::ostream& aFile, const SGVECTOR& aVector )
|
||||
{
|
||||
double x, y, z;
|
||||
aVector.GetVector( x, y, z );
|
||||
aFile.write( (char*) &x, sizeof( double ) );
|
||||
aFile.write( (char*) &y, sizeof( double ) );
|
||||
aFile.write( (char*) &z, sizeof( double ) );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::WriteColor( std::ostream& aFile, const SGCOLOR& aColor )
|
||||
{
|
||||
float r, g, b;
|
||||
aColor.GetColor( r, g, b );
|
||||
aFile.write( (char*) &r, sizeof( float ) );
|
||||
aFile.write( (char*) &g, sizeof( float ) );
|
||||
aFile.write( (char*) &b, sizeof( float ) );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
S3D::SGTYPES S3D::ReadTag( std::istream& aFile, std::string& aName )
|
||||
{
|
||||
char schar;
|
||||
aFile.get( schar );
|
||||
|
||||
if( '[' != schar )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; missing left bracket at "
|
||||
"position %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<int>( aFile.tellg() ) );
|
||||
|
||||
return S3D::SGTYPE_END;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
aFile.get( schar );
|
||||
|
||||
while( ']' != schar && aFile.good() )
|
||||
{
|
||||
name.push_back( schar );
|
||||
aFile.get( schar );
|
||||
}
|
||||
|
||||
if( schar != ']' )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; could not find right "
|
||||
"bracket" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return S3D::SGTYPE_END;
|
||||
}
|
||||
|
||||
aName = name;
|
||||
size_t upos = name.find( '_' );
|
||||
|
||||
if( std::string::npos == upos )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; no underscore in name '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return S3D::SGTYPE_END;
|
||||
}
|
||||
|
||||
name = name.substr( 0, upos );
|
||||
S3D::SGTYPES types[S3D::SGTYPE_END] = {
|
||||
SGTYPE_TRANSFORM,
|
||||
SGTYPE_APPEARANCE,
|
||||
SGTYPE_COLORS,
|
||||
SGTYPE_COLORINDEX,
|
||||
SGTYPE_FACESET,
|
||||
SGTYPE_COORDS,
|
||||
SGTYPE_COORDINDEX,
|
||||
SGTYPE_NORMALS,
|
||||
SGTYPE_SHAPE
|
||||
};
|
||||
|
||||
for( int i = 0; i < S3D::SGTYPE_END; ++i )
|
||||
{
|
||||
if( !name.compare( S3D::GetNodeTypeName( types[i] ) ) )
|
||||
return types[i];
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; no node type matching '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return S3D::SGTYPE_END;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::ReadPoint( std::istream& aFile, SGPOINT& aPoint )
|
||||
{
|
||||
aFile.read( (char*) &aPoint.x, sizeof( aPoint.x ) );
|
||||
aFile.read( (char*) &aPoint.y, sizeof( aPoint.y ) );
|
||||
aFile.read( (char*) &aPoint.z, sizeof( aPoint.z ) );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::ReadVector( std::istream& aFile, SGVECTOR& aVector )
|
||||
{
|
||||
double x, y, z;
|
||||
aFile.read( (char*) &x, sizeof( double ) );
|
||||
aFile.read( (char*) &y, sizeof( double ) );
|
||||
aFile.read( (char*) &z, sizeof( double ) );
|
||||
aVector.SetVector( x, y, z );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::ReadColor( std::istream& aFile, SGCOLOR& aColor )
|
||||
{
|
||||
float r, g, b;
|
||||
aFile.read( (char*) &r, sizeof( float ) );
|
||||
aFile.read( (char*) &g, sizeof( float ) );
|
||||
aFile.read( (char*) &b, sizeof( float ) );
|
||||
aColor.SetColor( r, g, b );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::degenerate( glm::dvec3* pts ) noexcept
|
||||
{
|
||||
double dx, dy, dz;
|
||||
|
||||
dx = pts[1].x - pts[0].x;
|
||||
dy = pts[1].y - pts[0].y;
|
||||
dz = pts[1].z - pts[0].z;
|
||||
|
||||
if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
|
||||
return true;
|
||||
|
||||
dx = pts[2].x - pts[0].x;
|
||||
dy = pts[2].y - pts[0].y;
|
||||
dz = pts[2].z - pts[0].z;
|
||||
|
||||
if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
|
||||
return true;
|
||||
|
||||
dx = pts[2].x - pts[1].x;
|
||||
dy = pts[2].y - pts[1].y;
|
||||
dz = pts[2].z - pts[1].z;
|
||||
|
||||
if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void calcTriad( glm::dvec3* pts, glm::dvec3& tri )
|
||||
{
|
||||
if( S3D::degenerate( pts ) )
|
||||
{
|
||||
// degenerate points should contribute nothing to the result
|
||||
tri = glm::dvec3( 0.0, 0.0, 0.0 );
|
||||
return;
|
||||
}
|
||||
|
||||
// normal * 2 * area
|
||||
tri = glm::cross( pts[1] - pts[0], pts[2] - pts[0] );
|
||||
}
|
||||
|
||||
|
||||
bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords, std::vector< int >& index,
|
||||
std::vector< SGVECTOR >& norms )
|
||||
{
|
||||
size_t vsize = coords.size();
|
||||
|
||||
if( vsize < 3 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] invalid vertex set (fewer than 3 "
|
||||
"vertices)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t isize = index.size();
|
||||
|
||||
if( 0 != isize % 3 || index.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] invalid index set (not multiple of 3)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !norms.empty() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] normals set is not empty" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map< int, std::list< glm::dvec3 > >vmap;
|
||||
|
||||
int p1, p2, p3;
|
||||
|
||||
// create the map of indices to facet sets
|
||||
for( size_t i = 0; i < isize; )
|
||||
{
|
||||
p1 = index[i++];
|
||||
p2 = index[i++];
|
||||
p3 = index[i++];
|
||||
|
||||
if( p1 < 0 || p1 >= (int)vsize || p2 < 0 || p2 >= (int)vsize || p3 < 0 || p3 >= (int)vsize )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::ostringstream ostr;
|
||||
ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
|
||||
ostr << " * [INFO] invalid index set; index out of bounds";
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s\n" ), ostr.str().c_str() );
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::dvec3 tri;
|
||||
glm::dvec3 trip[3];
|
||||
trip[0] = glm::dvec3( coords[p1].x, coords[p1].y, coords[p1].z );
|
||||
trip[1] = glm::dvec3( coords[p2].x, coords[p2].y, coords[p2].z );
|
||||
trip[2] = glm::dvec3( coords[p3].x, coords[p3].y, coords[p3].z );
|
||||
calcTriad( trip, tri );
|
||||
|
||||
std::map< int, std::list< glm::dvec3 > >::iterator ip = vmap.find( p1 );
|
||||
|
||||
if( ip != vmap.end() )
|
||||
{
|
||||
ip->second.push_back( tri );
|
||||
}
|
||||
else
|
||||
{
|
||||
vmap.emplace( p1, std::list < glm::dvec3 >( 1, tri ) );
|
||||
}
|
||||
|
||||
ip = vmap.find( p2 );
|
||||
|
||||
if( ip != vmap.end() )
|
||||
{
|
||||
ip->second.push_back( tri );
|
||||
}
|
||||
else
|
||||
{
|
||||
vmap.emplace( p2, std::list < glm::dvec3 >( 1, tri ) );
|
||||
}
|
||||
|
||||
ip = vmap.find( p3 );
|
||||
|
||||
if( ip != vmap.end() )
|
||||
{
|
||||
ip->second.push_back( tri );
|
||||
}
|
||||
else
|
||||
{
|
||||
vmap.emplace( p3, std::list < glm::dvec3 >( 1, tri ) );
|
||||
}
|
||||
}
|
||||
|
||||
std::map< int, std::list< glm::dvec3 > >::iterator sM = vmap.begin();
|
||||
std::map< int, std::list< glm::dvec3 > >::iterator eM = vmap.end();
|
||||
size_t idx = 0;
|
||||
|
||||
while( sM != eM )
|
||||
{
|
||||
size_t item = sM->first;
|
||||
|
||||
// assign any skipped coordinates a normal of (0,0,1)
|
||||
while( item > idx )
|
||||
{
|
||||
norms.emplace_back( 0, 0, 1 );
|
||||
++idx;
|
||||
}
|
||||
|
||||
std::list< glm::dvec3 >::iterator sT = sM->second.begin();
|
||||
std::list< glm::dvec3 >::iterator eT = sM->second.end();
|
||||
glm::dvec3 norm( 0.0, 0.0, 0.0 );
|
||||
|
||||
while( sT != eT )
|
||||
{
|
||||
norm += *sT;
|
||||
++sT;
|
||||
}
|
||||
|
||||
norms.emplace_back( norm.x, norm.y, norm.z );
|
||||
|
||||
++idx;
|
||||
++sM;
|
||||
}
|
||||
|
||||
if( norms.size() != coords.size() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] number of normals does not equal number "
|
||||
"of vertices" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_helpers.h
|
||||
*
|
||||
* Define a number of macros to aid in repetitious code which is probably best expressed
|
||||
* as a preprocessor macro rather than as a template. This header also declares a number
|
||||
* of functions which are only of use within the sg_* classes.
|
||||
*/
|
||||
|
||||
#ifndef SG_HELPERS_H
|
||||
#define SG_HELPERS_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "plugins/3dapi/sg_base.h"
|
||||
#include "plugins/3dapi/sg_types.h"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class SGNORMALS;
|
||||
class SGCOORDS;
|
||||
class SGCOORDINDEX;
|
||||
|
||||
|
||||
// Function to drop references within an SGNODE
|
||||
// The node being destroyed must remove itself from the object reference's
|
||||
// backpointer list in order to avoid a segfault.
|
||||
#define DROP_REFS( aType, aList ) \
|
||||
do \
|
||||
{ \
|
||||
std::vector<aType*>::iterator sL = aList.begin(); \
|
||||
std::vector<aType*>::iterator eL = aList.end(); \
|
||||
while( sL != eL ) \
|
||||
{ \
|
||||
( (SGNODE*) *sL )->delNodeRef( this ); \
|
||||
++sL; \
|
||||
} \
|
||||
aList.clear(); \
|
||||
} while( 0 )
|
||||
|
||||
|
||||
// Function to delete owned objects within an SGNODE
|
||||
// The owned object's parent is set to NULL before
|
||||
// deletion to avoid a redundant 'unlinkChildNode' call.
|
||||
#define DEL_OBJS( aType, aList ) \
|
||||
do \
|
||||
{ \
|
||||
std::vector<aType*>::iterator sL = aList.begin(); \
|
||||
std::vector<aType*>::iterator eL = aList.end(); \
|
||||
while( sL != eL ) \
|
||||
{ \
|
||||
( (SGNODE*) *sL )->SetParent( nullptr, false ); \
|
||||
delete *sL; \
|
||||
++sL; \
|
||||
} \
|
||||
aList.clear(); \
|
||||
} while( 0 )
|
||||
|
||||
|
||||
// Function to unlink a child or reference node when that child or
|
||||
// reference node is being destroyed.
|
||||
#define UNLINK_NODE( aNodeID, aType, aNode, aOwnedList, aRefList, isChild ) \
|
||||
do \
|
||||
{ \
|
||||
if( aNodeID == aNode->GetNodeType() ) \
|
||||
{ \
|
||||
std::vector<aType*>* oSL; \
|
||||
std::vector<aType*>::iterator sL; \
|
||||
std::vector<aType*>::iterator eL; \
|
||||
if( isChild ) \
|
||||
{ \
|
||||
oSL = &aOwnedList; \
|
||||
sL = oSL->begin(); \
|
||||
eL = oSL->end(); \
|
||||
while( sL != eL ) \
|
||||
{ \
|
||||
if( (SGNODE*) *sL == aNode ) \
|
||||
{ \
|
||||
oSL->erase( sL ); \
|
||||
return; \
|
||||
} \
|
||||
++sL; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
oSL = &aRefList; \
|
||||
sL = oSL->begin(); \
|
||||
eL = oSL->end(); \
|
||||
while( sL != eL ) \
|
||||
{ \
|
||||
if( (SGNODE*) *sL == aNode ) \
|
||||
{ \
|
||||
delNodeRef( this ); \
|
||||
oSL->erase( sL ); \
|
||||
return; \
|
||||
} \
|
||||
++sL; \
|
||||
} \
|
||||
} \
|
||||
return; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
|
||||
// Function to check a node type, check for an existing reference,
|
||||
// and add the node type to the reference list if applicable
|
||||
#define ADD_NODE( aNodeID, aType, aNode, aOwnedList, aRefList, isChild ) \
|
||||
do \
|
||||
{ \
|
||||
if( aNodeID == aNode->GetNodeType() ) \
|
||||
{ \
|
||||
std::vector<aType*>::iterator sL; \
|
||||
sL = std::find( aOwnedList.begin(), aOwnedList.end(), aNode ); \
|
||||
if( sL != aOwnedList.end() ) \
|
||||
return true; \
|
||||
sL = std::find( aRefList.begin(), aRefList.end(), aNode ); \
|
||||
if( sL != aRefList.end() ) \
|
||||
return true; \
|
||||
if( isChild ) \
|
||||
{ \
|
||||
SGNODE* ppn = (SGNODE*) aNode->GetParent(); \
|
||||
if( nullptr != ppn ) \
|
||||
{ \
|
||||
if( this != ppn ) \
|
||||
{ \
|
||||
std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; \
|
||||
std::cerr << " * [BUG] object '" << aNode->GetName(); \
|
||||
std::cerr << "' has multiple parents '" << ppn->GetName() << "', '"; \
|
||||
std::cerr << m_Name << "'\n"; \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
aOwnedList.push_back( (aType*) aNode ); \
|
||||
aNode->SetParent( this, false ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/*if( nullptr == aNode->GetParent() ) { \
|
||||
std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; \
|
||||
std::cerr << " * [BUG] object '" << aNode->GetName(); \
|
||||
std::cerr << "' has no parent\n"; \
|
||||
std::cerr << " * [INFO] possible copy assignment or copy constructor bug\n"; \
|
||||
return false; \
|
||||
} */ \
|
||||
aRefList.push_back( (aType*) aNode ); \
|
||||
aNode->addNodeRef( this ); \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
|
||||
// Function to find a node object given a (non-unique) node name
|
||||
#define FIND_NODE( aType, aName, aNodeList, aCallingNode ) \
|
||||
do \
|
||||
{ \
|
||||
std::vector<aType*>::iterator sLA = aNodeList.begin(); \
|
||||
std::vector<aType*>::iterator eLA = aNodeList.end(); \
|
||||
SGNODE* psg = nullptr; \
|
||||
while( sLA != eLA ) \
|
||||
{ \
|
||||
if( (SGNODE*) *sLA != aCallingNode ) \
|
||||
{ \
|
||||
psg = (SGNODE*) ( *sLA )->FindNode( aName, this ); \
|
||||
if( nullptr != psg ) \
|
||||
return psg; \
|
||||
} \
|
||||
++sLA; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
|
||||
namespace S3D
|
||||
{
|
||||
bool degenerate( glm::dvec3* pts ) noexcept;
|
||||
|
||||
//
|
||||
// Normals calculations from triangles
|
||||
//
|
||||
|
||||
/*
|
||||
* Take an array of 3D coordinates and its corresponding index set and calculates
|
||||
* the normals assuming that indices are given in CCW order.
|
||||
*
|
||||
* Care must be taken in using this function to ensure that:
|
||||
* -# All coordinates are indexed; unindexed coordinates are assigned normal(0,0,1);
|
||||
* when dealing with VRML models which may list and reuse one large coordinate set it
|
||||
* is necessary to gather all index sets and perform this operation only once.
|
||||
* -# Index sets must represent triangles (multiple of 3 indices) and must not be
|
||||
* degenerate, that is all indices and coordinates in a triad must be unique.
|
||||
*
|
||||
* @param coords is the array of 3D vertices.
|
||||
* @param index is the array of 3x vertex indices (triads).
|
||||
* @param norms is an empty array which holds the normals corresponding to each vector.
|
||||
* @return true on success; otherwise false.
|
||||
*/
|
||||
bool CalcTriangleNormals( std::vector< SGPOINT > coords, std::vector< int >& index,
|
||||
std::vector< SGVECTOR >& norms );
|
||||
|
||||
// formats a floating point number for text output to a VRML file
|
||||
void FormatFloat( std::string& result, double value );
|
||||
|
||||
// format orientation data for VRML output
|
||||
void FormatOrientation( std::string& result, const SGVECTOR& axis, double rotation );
|
||||
|
||||
// format point data for VRML output
|
||||
void FormatPoint( std::string& result, const SGPOINT& point );
|
||||
|
||||
// format vector data for VRML output
|
||||
void FormatVector( std::string& result, const SGVECTOR& aVector );
|
||||
|
||||
// format Color data for VRML output
|
||||
void FormatColor( std::string& result, const SGCOLOR& aColor );
|
||||
|
||||
//
|
||||
// Cache related WRITE functions
|
||||
//
|
||||
|
||||
// write out an XYZ vertex
|
||||
bool WritePoint( std::ostream& aFile, const SGPOINT& aPoint );
|
||||
|
||||
// write out a unit vector
|
||||
bool WriteVector( std::ostream& aFile, const SGVECTOR& aVector );
|
||||
|
||||
// write out an RGB color
|
||||
bool WriteColor( std::ostream& aFile, const SGCOLOR& aColor );
|
||||
|
||||
/**
|
||||
* Read the text tag of a binary cache file which is the NodeTag and unique ID number combined.
|
||||
*
|
||||
* @param aFile is a binary file open for reading.
|
||||
* @param aName will hold the tag name on successful return.
|
||||
* @return will be the NodeType which the tag represents or S3D::SGTYPES::SGTYPE_END on
|
||||
* failure.
|
||||
*/
|
||||
S3D::SGTYPES ReadTag( std::istream& aFile, std::string& aName );
|
||||
|
||||
// read an XYZ vertex
|
||||
bool ReadPoint( std::istream& aFile, SGPOINT& aPoint );
|
||||
|
||||
// read a unit vector
|
||||
bool ReadVector( std::istream& aFile, SGVECTOR& aVector );
|
||||
|
||||
// read an RGB color
|
||||
bool ReadColor( std::istream& aFile, SGCOLOR& aColor );
|
||||
}
|
||||
|
||||
#endif // SG_HELPERS_H
|
||||
@@ -1,329 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_index.h"
|
||||
|
||||
|
||||
SGINDEX::SGINDEX( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] inappropriate parent to SGINDEX (type "
|
||||
"'%d')" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeType() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGINDEX::~SGINDEX()
|
||||
{
|
||||
index.clear();
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGFACESET may be parent to a SGINDEX and derived types
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGINDEX::FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void SGINDEX::unlinkChildNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
// Node should have no children or refs.
|
||||
wxCHECK( false, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
void SGINDEX::unlinkRefNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
// Node should have no children or refs.
|
||||
wxCHECK( false, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::AddRefNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
// Node should have no children or refs.
|
||||
wxCHECK( false, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::AddChildNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
// Node should have no children or refs.
|
||||
wxCHECK( false, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::GetIndices( size_t& nIndices, int*& aIndexList )
|
||||
{
|
||||
if( index.empty() )
|
||||
{
|
||||
nIndices = 0;
|
||||
aIndexList = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
nIndices = index.size();
|
||||
aIndexList = & index[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SGINDEX::SetIndices( size_t nIndices, int* aIndexList )
|
||||
{
|
||||
index.clear();
|
||||
|
||||
if( 0 == nIndices || nullptr == aIndexList )
|
||||
return;
|
||||
|
||||
for( size_t i = 0; i < nIndices; ++i )
|
||||
index.push_back( aIndexList[i] );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void SGINDEX::AddIndex( int aIndex )
|
||||
{
|
||||
index.push_back( aIndex );
|
||||
}
|
||||
|
||||
|
||||
void SGINDEX::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( index.empty() )
|
||||
return false;
|
||||
|
||||
if( S3D::SGTYPE_COORDINDEX == m_SGtype )
|
||||
return writeCoordIndex( aFile );
|
||||
|
||||
return writeColorIndex( aFile );
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::writeCoordIndex( std::ostream& aFile )
|
||||
{
|
||||
size_t n = index.size();
|
||||
|
||||
wxCHECK_MSG( n % 3 == 0, false, wxT( "Coordinate index is not divisible by three (violates "
|
||||
"triangle constraint)" ) );
|
||||
|
||||
aFile << " coordIndex [\n ";
|
||||
|
||||
// indices to control formatting
|
||||
int nv0 = 0;
|
||||
int nv1 = 0;
|
||||
|
||||
for( size_t i = 0; i < n; )
|
||||
{
|
||||
aFile << index[i];
|
||||
++i;
|
||||
|
||||
if( ++nv0 == 3 )
|
||||
{
|
||||
aFile << ",-1";
|
||||
++nv1;
|
||||
nv0 = 0;
|
||||
}
|
||||
|
||||
if( i < n )
|
||||
{
|
||||
aFile << ",";
|
||||
|
||||
if( nv1 == 8 )
|
||||
{
|
||||
nv1 = 0;
|
||||
aFile << "\n ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aFile << "]\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::writeColorIndex( std::ostream& aFile )
|
||||
{
|
||||
aFile << " colorIndex [\n ";
|
||||
return writeIndexList( aFile );
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::writeIndexList( std::ostream& aFile )
|
||||
{
|
||||
// index to control formatting
|
||||
int nv = 0;
|
||||
size_t n = index.size();
|
||||
|
||||
for( size_t i = 0; i < n; )
|
||||
{
|
||||
aFile << index[i];
|
||||
++i;
|
||||
|
||||
if( i < n )
|
||||
{
|
||||
aFile << ",";
|
||||
|
||||
if( ++nv == 20 )
|
||||
{
|
||||
aFile << "\n ";
|
||||
nv = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aFile << "]\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK( m_Parent, false );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
size_t npts = index.size();
|
||||
aFile.write( (char*)&npts, sizeof(size_t) );
|
||||
|
||||
for( size_t i = 0; i < npts; ++i )
|
||||
aFile.write( (char*)&index[i], sizeof(int) );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGINDEX::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
wxCHECK( index.empty(), false );
|
||||
|
||||
size_t npts;
|
||||
aFile.read( (char*)&npts, sizeof(size_t) );
|
||||
int tmp;
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
for( size_t i = 0; i < npts; ++i )
|
||||
{
|
||||
aFile.read( (char*) &tmp, sizeof( int ) );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
index.push_back( tmp );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_index.h
|
||||
*/
|
||||
|
||||
#ifndef SG_INDEX_H
|
||||
#define SG_INDEX_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
/**
|
||||
* Define a generic index interface for a scenegraph object.
|
||||
*/
|
||||
class SGINDEX : public SGNODE
|
||||
{
|
||||
public:
|
||||
SGINDEX( SGNODE* aParent );
|
||||
virtual ~SGINDEX();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
SGNODE* FindNode(const char* aNodeName, const SGNODE* aCaller) noexcept override;
|
||||
bool AddRefNode( SGNODE* aNode ) noexcept override;
|
||||
bool AddChildNode( SGNODE* aNode ) noexcept override;
|
||||
|
||||
void unlinkChildNode( const SGNODE* aCaller ) noexcept override;
|
||||
void unlinkRefNode( const SGNODE* aCaller ) noexcept override;
|
||||
|
||||
/**
|
||||
* Retrieve the number of indices and a pointer to the list.
|
||||
*
|
||||
* @note The returned pointer may be invalidated by future operations on the SGNODE. The
|
||||
* caller must make immediate use of the data and must not rely on the pointer's
|
||||
* validity in the future.
|
||||
*
|
||||
* @param nIndices will hold the number of indices in the list.
|
||||
* @param aIndexList will store a pointer to the data.
|
||||
* @return true if there was available data (nIndices > 0) otherwise false.
|
||||
*/
|
||||
bool GetIndices( size_t& nIndices, int*& aIndexList );
|
||||
|
||||
/**
|
||||
* Set the number of indices and creates a copy of the given index data.
|
||||
*
|
||||
* @param nIndices the number of indices to be stored.
|
||||
* @param aIndexList the index data.
|
||||
*/
|
||||
void SetIndices( size_t nIndices, int* aIndexList );
|
||||
|
||||
|
||||
/**
|
||||
* Add a single index to the list.
|
||||
*
|
||||
* @param aIndex is the index to add.
|
||||
*/
|
||||
void AddIndex( int aIndex );
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
protected:
|
||||
bool writeCoordIndex( std::ostream& aFile );
|
||||
bool writeColorIndex( std::ostream& aFile );
|
||||
bool writeIndexList( std::ostream& aFile );
|
||||
|
||||
public:
|
||||
// for internal SG consumption only
|
||||
std::vector< int > index;
|
||||
};
|
||||
|
||||
#endif // SG_INDEX_H
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
#include "plugins/3dapi/c3dmodel.h"
|
||||
|
||||
|
||||
static const std::string node_names[S3D::SGTYPE_END + 1] = {
|
||||
"TXFM",
|
||||
"APP",
|
||||
"COL",
|
||||
"COLIDX",
|
||||
"FACE",
|
||||
"COORD",
|
||||
"COORDIDX",
|
||||
"NORM",
|
||||
"SHAPE",
|
||||
"INVALID"
|
||||
};
|
||||
|
||||
|
||||
static unsigned int node_counts[S3D::SGTYPE_END] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
|
||||
|
||||
char const* S3D::GetNodeTypeName( S3D::SGTYPES aType ) noexcept
|
||||
{
|
||||
return node_names[aType].c_str();
|
||||
}
|
||||
|
||||
|
||||
static void getNodeName( S3D::SGTYPES nodeType, std::string& aName )
|
||||
{
|
||||
if( nodeType < 0 || nodeType >= S3D::SGTYPE_END )
|
||||
{
|
||||
aName = node_names[S3D::SGTYPE_END];
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int seqNum = node_counts[nodeType];
|
||||
++node_counts[nodeType];
|
||||
|
||||
std::ostringstream ostr;
|
||||
ostr << node_names[nodeType] << "_" << seqNum;
|
||||
aName = ostr.str();
|
||||
}
|
||||
|
||||
|
||||
SGNODE::SGNODE( SGNODE* aParent )
|
||||
{
|
||||
m_Parent = aParent;
|
||||
m_Association = nullptr;
|
||||
m_written = false;
|
||||
m_SGtype = S3D::SGTYPE_END;
|
||||
}
|
||||
|
||||
|
||||
SGNODE::~SGNODE()
|
||||
{
|
||||
if( m_Parent )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
if( m_Association )
|
||||
*m_Association = nullptr;
|
||||
|
||||
std::list< SGNODE* >::iterator sBP = m_BackPointers.begin();
|
||||
std::list< SGNODE* >::iterator eBP = m_BackPointers.end();
|
||||
|
||||
while( sBP != eBP )
|
||||
{
|
||||
(*sBP)->unlinkRefNode( this );
|
||||
++sBP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
S3D::SGTYPES SGNODE::GetNodeType( void ) const noexcept
|
||||
{
|
||||
return m_SGtype;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGNODE::GetParent( void ) const noexcept
|
||||
{
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
|
||||
bool SGNODE::SwapParent( SGNODE* aNewParent )
|
||||
{
|
||||
if( aNewParent == m_Parent )
|
||||
return true;
|
||||
|
||||
if( nullptr == aNewParent )
|
||||
return false;
|
||||
|
||||
if( nullptr == m_Parent )
|
||||
{
|
||||
if( aNewParent->AddChildNode( this ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aNewParent->GetNodeType() != m_Parent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
SGNODE* oldParent = m_Parent;
|
||||
m_Parent->unlinkChildNode( this );
|
||||
m_Parent = nullptr;
|
||||
aNewParent->unlinkRefNode( this );
|
||||
aNewParent->AddChildNode( this );
|
||||
oldParent->AddRefNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* SGNODE::GetName( void )
|
||||
{
|
||||
if( m_Name.empty() )
|
||||
getNodeName( m_SGtype, m_Name );
|
||||
|
||||
return m_Name.c_str();
|
||||
}
|
||||
|
||||
|
||||
void SGNODE::SetName( const char* aName )
|
||||
{
|
||||
if( nullptr == aName || 0 == aName[0] )
|
||||
getNodeName( m_SGtype, m_Name );
|
||||
else
|
||||
m_Name = aName;
|
||||
}
|
||||
|
||||
|
||||
const char* SGNODE::GetNodeTypeName( S3D::SGTYPES aNodeType ) const noexcept
|
||||
{
|
||||
return node_names[aNodeType].c_str();
|
||||
}
|
||||
|
||||
|
||||
void SGNODE::addNodeRef( SGNODE* aNode )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return;
|
||||
|
||||
std::list< SGNODE* >::iterator np =
|
||||
std::find( m_BackPointers.begin(), m_BackPointers.end(), aNode );
|
||||
|
||||
if( np != m_BackPointers.end() )
|
||||
return;
|
||||
|
||||
m_BackPointers.push_back( aNode );
|
||||
}
|
||||
|
||||
|
||||
void SGNODE::delNodeRef( const SGNODE* aNode )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return;
|
||||
|
||||
std::list< SGNODE* >::iterator np =
|
||||
std::find( m_BackPointers.begin(), m_BackPointers.end(), aNode );
|
||||
|
||||
if( np != m_BackPointers.end() )
|
||||
{
|
||||
m_BackPointers.erase( np );
|
||||
return;
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] delNodeRef() did not find its target, this "
|
||||
"node type %d, referenced node type %d" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
m_SGtype,
|
||||
aNode->GetNodeType() );
|
||||
}
|
||||
|
||||
|
||||
void SGNODE::AssociateWrapper( SGNODE** aWrapperRef ) noexcept
|
||||
{
|
||||
wxCHECK( aWrapperRef && *aWrapperRef == this, /* void */ );
|
||||
|
||||
// if there is an existing association then break it and emit a warning
|
||||
// just in case the behavior is undesired
|
||||
if( m_Association )
|
||||
{
|
||||
*m_Association = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [WARNING] association being broken with "
|
||||
"previous wrapper" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
}
|
||||
|
||||
m_Association = aWrapperRef;
|
||||
}
|
||||
|
||||
void SGNODE::DisassociateWrapper( SGNODE** aWrapperRef ) noexcept
|
||||
{
|
||||
if( !m_Association )
|
||||
return;
|
||||
|
||||
wxCHECK( aWrapperRef, /* void */ );
|
||||
|
||||
wxCHECK( *aWrapperRef == *m_Association && aWrapperRef == m_Association, /* void */ );
|
||||
|
||||
m_Association = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void SGNODE::ResetNodeIndex( void ) noexcept
|
||||
{
|
||||
for( int i = 0; i < (int)S3D::SGTYPE_END; ++i )
|
||||
node_counts[i] = 1;
|
||||
}
|
||||
|
||||
|
||||
bool S3D::GetMatIndex( MATLIST& aList, SGNODE* aNode, int& aIndex )
|
||||
{
|
||||
aIndex = 0;
|
||||
|
||||
wxCHECK( aNode && S3D::SGTYPE_APPEARANCE == aNode->GetNodeType(), false );
|
||||
|
||||
SGAPPEARANCE* node = (SGAPPEARANCE*)aNode;
|
||||
|
||||
std::map< SGAPPEARANCE const*, int >::iterator it = aList.matmap.find( node );
|
||||
|
||||
if( it != aList.matmap.end() )
|
||||
{
|
||||
aIndex = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
int idx = (int)aList.matorder.size();
|
||||
aList.matorder.push_back( node );
|
||||
aList.matmap.emplace( node, idx );
|
||||
aIndex = idx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void S3D::INIT_SMATERIAL( SMATERIAL& aMaterial )
|
||||
{
|
||||
aMaterial = {};
|
||||
}
|
||||
|
||||
|
||||
void S3D::INIT_SMESH( SMESH& aMesh ) noexcept
|
||||
{
|
||||
aMesh = {};
|
||||
}
|
||||
|
||||
|
||||
void S3D::INIT_S3DMODEL( S3DMODEL& aModel ) noexcept
|
||||
{
|
||||
aModel = {};
|
||||
}
|
||||
|
||||
|
||||
void S3D::FREE_SMESH( SMESH& aMesh ) noexcept
|
||||
{
|
||||
if( nullptr != aMesh.m_Positions )
|
||||
{
|
||||
delete [] aMesh.m_Positions;
|
||||
aMesh.m_Positions = nullptr;
|
||||
}
|
||||
|
||||
if( nullptr != aMesh.m_Normals )
|
||||
{
|
||||
delete [] aMesh.m_Normals;
|
||||
aMesh.m_Normals = nullptr;
|
||||
}
|
||||
|
||||
if( nullptr != aMesh.m_Texcoords )
|
||||
{
|
||||
delete [] aMesh.m_Texcoords;
|
||||
aMesh.m_Texcoords = nullptr;
|
||||
}
|
||||
|
||||
if( nullptr != aMesh.m_Color )
|
||||
{
|
||||
delete [] aMesh.m_Color;
|
||||
aMesh.m_Color = nullptr;
|
||||
}
|
||||
|
||||
if( nullptr != aMesh.m_FaceIdx )
|
||||
{
|
||||
delete [] aMesh.m_FaceIdx;
|
||||
aMesh.m_FaceIdx = nullptr;
|
||||
}
|
||||
|
||||
aMesh.m_VertexSize = 0;
|
||||
aMesh.m_FaceIdxSize = 0;
|
||||
aMesh.m_MaterialIdx = 0;
|
||||
}
|
||||
|
||||
|
||||
void S3D::FREE_S3DMODEL( S3DMODEL& aModel )
|
||||
{
|
||||
if( nullptr != aModel.m_Materials )
|
||||
{
|
||||
delete [] aModel.m_Materials;
|
||||
aModel.m_Materials = nullptr;
|
||||
}
|
||||
|
||||
aModel.m_MaterialsSize = 0;
|
||||
|
||||
if( nullptr != aModel.m_Meshes )
|
||||
{
|
||||
for( unsigned int i = 0; i < aModel.m_MeshesSize; ++i )
|
||||
FREE_SMESH( aModel.m_Meshes[i] );
|
||||
|
||||
delete [] aModel.m_Meshes;
|
||||
aModel.m_Meshes = nullptr;
|
||||
}
|
||||
|
||||
aModel.m_MeshesSize = 0;
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_node.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SG_NODE_H
|
||||
#define SG_NODE_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "plugins/3dapi/c3dmodel.h"
|
||||
#include "plugins/3dapi/sg_base.h"
|
||||
#include "plugins/3dapi/sg_types.h"
|
||||
|
||||
class SGNODE;
|
||||
class SGAPPEARANCE;
|
||||
|
||||
namespace S3D
|
||||
{
|
||||
/**
|
||||
* Return the name of the given type of node
|
||||
*/
|
||||
char const* GetNodeTypeName( S3D::SGTYPES aType ) noexcept;
|
||||
|
||||
struct MATLIST
|
||||
{
|
||||
std::vector< SGAPPEARANCE const* > matorder; // materials in order of addition
|
||||
std::map< SGAPPEARANCE const*, int > matmap; // mapping from material to index
|
||||
};
|
||||
|
||||
bool GetMatIndex( MATLIST& aList, SGNODE* aNode, int& aIndex );
|
||||
|
||||
void INIT_SMATERIAL( SMATERIAL& aMaterial );
|
||||
void INIT_SMESH( SMESH& aMesh ) noexcept;
|
||||
void INIT_S3DMODEL( S3DMODEL& aModel ) noexcept;
|
||||
|
||||
void FREE_SMESH( SMESH& aMesh) noexcept;
|
||||
void FREE_S3DMODEL( S3DMODEL& aModel );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The base class of all Scene Graph nodes.
|
||||
*/
|
||||
class SGNODE
|
||||
{
|
||||
public:
|
||||
SGNODE( SGNODE* aParent );
|
||||
virtual ~SGNODE();
|
||||
|
||||
/**
|
||||
* Return the type of this node instance.
|
||||
*/
|
||||
S3D::SGTYPES GetNodeType( void ) const noexcept;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the parent SGNODE of this object or NULL if the object has
|
||||
* no parent (ie. top level transform).
|
||||
*/
|
||||
SGNODE* GetParent( void ) const noexcept;
|
||||
|
||||
/**
|
||||
* Set the parent #SGNODE of this object.
|
||||
*
|
||||
* @param aParent [in] is the desired parent node
|
||||
* @return true if the operation succeeds; false if the given node is not allowed to
|
||||
* be a parent to the derived object.
|
||||
*/
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) = 0;
|
||||
|
||||
/**
|
||||
* Swap the ownership with the given parent.
|
||||
*
|
||||
* This operation may be required when reordering nodes for optimization.
|
||||
*
|
||||
* @param aNewParent will become the new parent to the object; it must be the same type
|
||||
* as the parent of this instance.
|
||||
*/
|
||||
bool SwapParent( SGNODE* aNewParent );
|
||||
|
||||
const char* GetName( void );
|
||||
void SetName(const char *aName);
|
||||
|
||||
const char * GetNodeTypeName( S3D::SGTYPES aNodeType ) const noexcept;
|
||||
|
||||
/**
|
||||
* Search the tree of linked nodes and return a reference to the first node found with
|
||||
* the given name.
|
||||
*
|
||||
* The reference is then typically added to another node via AddRefNode().
|
||||
*
|
||||
* @param aNodeName is the name of the node to search for.
|
||||
* @param aCaller is a pointer to the node invoking this function.
|
||||
* @return is a valid node pointer on success, otherwise NULL.
|
||||
*/
|
||||
virtual SGNODE* FindNode( const char *aNodeName, const SGNODE *aCaller ) = 0;
|
||||
|
||||
virtual bool AddRefNode( SGNODE* aNode ) = 0;
|
||||
|
||||
virtual bool AddChildNode( SGNODE* aNode ) = 0;
|
||||
|
||||
/**
|
||||
* Associate this object with a handle to itself.
|
||||
*
|
||||
* The handle is typically held by an IFSG* wrapper and the pointer which it refers to
|
||||
* is set to NULL upon destruction of this object. This mechanism provides a scheme
|
||||
* by which a wrapper can be notified of the destruction of the object which it wraps.
|
||||
*/
|
||||
void AssociateWrapper( SGNODE** aWrapperRef ) noexcept;
|
||||
|
||||
/**
|
||||
* Remove the association between an IFSG* wrapper object and this object.
|
||||
*/
|
||||
void DisassociateWrapper( SGNODE** aWrapperRef ) noexcept;
|
||||
|
||||
/**
|
||||
* Reset the global SG* node indices in preparation for write operations.
|
||||
*/
|
||||
void ResetNodeIndex( void ) noexcept;
|
||||
|
||||
/**
|
||||
* Rename a node and all its child nodes in preparation for write operations.
|
||||
*/
|
||||
virtual void ReNameNodes( void ) = 0;
|
||||
|
||||
/**
|
||||
* Writes this node's data to a VRML file.
|
||||
*
|
||||
* This includes all data of child and referenced nodes.
|
||||
*/
|
||||
virtual bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) = 0;
|
||||
|
||||
/**
|
||||
* Write this node's data to a binary cache file.
|
||||
*
|
||||
* The data includes all data of children and references to children. If this function
|
||||
* is invoked by the user, parentNode must be set to NULL in order to ensure coherent data.
|
||||
*/
|
||||
virtual bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) = 0;
|
||||
|
||||
/**
|
||||
* Reads binary format data from a cache file.
|
||||
*
|
||||
* To read a cache file, open the file for reading and invoke this function from a new
|
||||
* #SCENEGRAPH node.
|
||||
*/
|
||||
virtual bool ReadCache( std::istream& aFile, SGNODE* parentNode ) = 0;
|
||||
|
||||
/**
|
||||
* Remove references to an owned child.
|
||||
*
|
||||
* This is invoked by the child upon destruction to ensure that the parent has no
|
||||
* invalid references.
|
||||
*
|
||||
* @param aNode is the child which is being deleted.
|
||||
*/
|
||||
virtual void unlinkChildNode( const SGNODE* aNode ) = 0;
|
||||
|
||||
/**
|
||||
* Remove pointers to a referenced node.
|
||||
*
|
||||
* This is invoked by the referenced node upon destruction to ensure that the referring
|
||||
* node has no invalid references.
|
||||
*
|
||||
* @param aNode is the node which is being deleted.
|
||||
*/
|
||||
virtual void unlinkRefNode( const SGNODE* aNode ) = 0;
|
||||
|
||||
/**
|
||||
* Add a pointer to a node which references this node, but does not own.
|
||||
*
|
||||
* Such back-pointers are required to ensure that invalidated references are removed
|
||||
* when a node is deleted.
|
||||
*
|
||||
* @param aNode is the node holding a reference to this object.
|
||||
*/
|
||||
void addNodeRef( SGNODE* aNode );
|
||||
|
||||
/**
|
||||
* Remove a pointer to a node which references this node, but does not own.
|
||||
*
|
||||
* @param aNode is the node holding a reference to this object.
|
||||
*/
|
||||
void delNodeRef( const SGNODE* aNode );
|
||||
|
||||
/**
|
||||
* Return true if the object had already been written to a cache file or VRML file
|
||||
*
|
||||
* For internal use only.
|
||||
*/
|
||||
bool isWritten( void ) noexcept
|
||||
{
|
||||
return m_written;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list< SGNODE* > m_BackPointers; ///< nodes which hold a reference to this.
|
||||
SGNODE* m_Parent; ///< Pointer to parent node; may be NULL for top level transform.
|
||||
S3D::SGTYPES m_SGtype; ///< Type of Scene Graph node.
|
||||
std::string m_Name; ///< name to use for referencing the entity by name.
|
||||
bool m_written; ///< Set to true when the object has been written after a ReNameNodes().
|
||||
|
||||
private:
|
||||
SGNODE** m_Association; ///< Handle to the instance held by a wrapper.
|
||||
};
|
||||
|
||||
#endif // SG_NODE_H
|
||||
@@ -1,299 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_normals.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
|
||||
|
||||
SGNORMALS::SGNORMALS( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_NORMALS;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] inappropriate parent to SGNORMALS "
|
||||
"(type %d)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_FACESET == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGNORMALS::~SGNORMALS()
|
||||
{
|
||||
norms.clear();
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGFACESET may be parent to a SGNORMALS
|
||||
if( nullptr != aParent && S3D::SGTYPE_FACESET != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGNORMALS::FindNode( const char* aNodeName, const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void SGNORMALS::unlinkChildNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK( false, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
void SGNORMALS::unlinkRefNode( const SGNODE* aCaller ) noexcept
|
||||
{
|
||||
wxCHECK( false, /* void */ );
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::AddRefNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( false, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::AddChildNode( SGNODE* aNode ) noexcept
|
||||
{
|
||||
wxCHECK( false, false );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::GetNormalList( size_t& aListSize, SGVECTOR*& aNormalList )
|
||||
{
|
||||
if( norms.empty() )
|
||||
{
|
||||
aListSize = 0;
|
||||
aNormalList = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
aListSize = norms.size();
|
||||
aNormalList = &norms[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SGNORMALS::SetNormalList( size_t aListSize, const SGVECTOR* aNormalList )
|
||||
{
|
||||
norms.clear();
|
||||
|
||||
if( 0 == aListSize || nullptr == aNormalList )
|
||||
return;
|
||||
|
||||
for( int i = 0; i < (int)aListSize; ++i )
|
||||
norms.push_back( aNormalList[i] );
|
||||
}
|
||||
|
||||
|
||||
void SGNORMALS::AddNormal( double aXValue, double aYValue, double aZValue )
|
||||
{
|
||||
norms.emplace_back( aXValue, aYValue, aZValue );
|
||||
}
|
||||
|
||||
|
||||
void SGNORMALS::AddNormal( const SGVECTOR& aNormal )
|
||||
{
|
||||
norms.push_back( aNormal );
|
||||
}
|
||||
|
||||
|
||||
void SGNORMALS::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( norms.empty() )
|
||||
return false;
|
||||
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << " normal DEF " << GetName() << " Normal { vector [\n ";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " normal USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " normal Normal { vector [\n ";
|
||||
}
|
||||
|
||||
std::string tmp;
|
||||
size_t n = norms.size();
|
||||
bool nline = false;
|
||||
|
||||
for( size_t i = 0; i < n; )
|
||||
{
|
||||
S3D::FormatVector( tmp, norms[i] );
|
||||
aFile << tmp ;
|
||||
++i;
|
||||
|
||||
if( i < n )
|
||||
{
|
||||
aFile << ",";
|
||||
|
||||
if( nline )
|
||||
{
|
||||
aFile << "\n ";
|
||||
nline = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nline = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
aFile << "] }\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK( m_Parent, false );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
size_t npts = norms.size();
|
||||
aFile.write( (char*)&npts, sizeof(size_t) );
|
||||
|
||||
for( size_t i = 0; i < npts; ++i )
|
||||
S3D::WriteVector( aFile, norms[i] );
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGNORMALS::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
wxCHECK( norms.empty(), false );
|
||||
|
||||
size_t npts;
|
||||
aFile.read( (char*) &npts, sizeof( size_t ) );
|
||||
SGVECTOR tmp;
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
for( size_t i = 0; i < npts; ++i )
|
||||
{
|
||||
if( !S3D::ReadVector( aFile, tmp ) || aFile.fail() )
|
||||
return false;
|
||||
|
||||
norms.push_back( tmp );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_normals.h
|
||||
*/
|
||||
|
||||
#ifndef SG_NORMALS_H
|
||||
#define SG_NORMALS_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
/**
|
||||
* Define a set of vertex normals for a scene graph object
|
||||
*/
|
||||
class SGNORMALS : public SGNODE
|
||||
{
|
||||
public:
|
||||
SGNORMALS( SGNODE* aParent );
|
||||
virtual ~SGNORMALS();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
SGNODE* FindNode( const char* aNodeName, const SGNODE* aCaller ) noexcept override;
|
||||
bool AddRefNode( SGNODE* aNode ) noexcept override;
|
||||
bool AddChildNode( SGNODE* aNode ) noexcept override;
|
||||
|
||||
bool GetNormalList( size_t& aListSize, SGVECTOR*& aNormalList );
|
||||
void SetNormalList( size_t aListSize, const SGVECTOR* aNormalList );
|
||||
void AddNormal( double aXValue, double aYValue, double aZValue );
|
||||
void AddNormal( const SGVECTOR& aNormal );
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
void unlinkChildNode( const SGNODE* aNode ) noexcept override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) noexcept override;
|
||||
|
||||
std::vector< SGVECTOR > norms;
|
||||
};
|
||||
|
||||
#endif // SG_NORMALS_H
|
||||
@@ -1,783 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include "3d_cache/sg/sg_shape.h"
|
||||
#include "3d_cache/sg/sg_faceset.h"
|
||||
#include "3d_cache/sg/sg_appearance.h"
|
||||
#include "3d_cache/sg/sg_helpers.h"
|
||||
#include "3d_cache/sg/sg_coordindex.h"
|
||||
#include "3d_cache/sg/sg_coords.h"
|
||||
#include "3d_cache/sg/sg_colors.h"
|
||||
#include "3d_cache/sg/sg_normals.h"
|
||||
|
||||
|
||||
SGSHAPE::SGSHAPE( SGNODE* aParent ) : SGNODE( aParent )
|
||||
{
|
||||
m_SGtype = S3D::SGTYPE_SHAPE;
|
||||
m_Appearance = nullptr;
|
||||
m_RAppearance = nullptr;
|
||||
m_FaceSet = nullptr;
|
||||
m_RFaceSet = nullptr;
|
||||
|
||||
if( nullptr != aParent && S3D::SGTYPE_TRANSFORM != aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent = nullptr;
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] inappropriate parent to SGSHAPE (type %d)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aParent->GetNodeType() );
|
||||
}
|
||||
else if( nullptr != aParent && S3D::SGTYPE_TRANSFORM == aParent->GetNodeType() )
|
||||
{
|
||||
m_Parent->AddChildNode( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SGSHAPE::~SGSHAPE()
|
||||
{
|
||||
// drop references
|
||||
if( m_RAppearance )
|
||||
{
|
||||
m_RAppearance->delNodeRef( this );
|
||||
m_RAppearance = nullptr;
|
||||
}
|
||||
|
||||
if( m_RFaceSet )
|
||||
{
|
||||
m_RFaceSet->delNodeRef( this );
|
||||
m_RFaceSet = nullptr;
|
||||
}
|
||||
|
||||
// delete objects
|
||||
if( m_Appearance )
|
||||
{
|
||||
m_Appearance->SetParent( nullptr, false );
|
||||
delete m_Appearance;
|
||||
m_Appearance = nullptr;
|
||||
}
|
||||
|
||||
if( m_FaceSet )
|
||||
{
|
||||
m_FaceSet->SetParent( nullptr, false );
|
||||
delete m_FaceSet;
|
||||
m_FaceSet = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::SetParent( SGNODE* aParent, bool notify )
|
||||
{
|
||||
if( nullptr != m_Parent )
|
||||
{
|
||||
if( aParent == m_Parent )
|
||||
return true;
|
||||
|
||||
// handle the change in parents
|
||||
if( notify )
|
||||
m_Parent->unlinkChildNode( this );
|
||||
|
||||
m_Parent = nullptr;
|
||||
|
||||
if( nullptr == aParent )
|
||||
return true;
|
||||
}
|
||||
|
||||
// only a SGTRANSFORM may be parent to a SGSHAPE
|
||||
if( nullptr != aParent && S3D::SGTYPE_TRANSFORM != aParent->GetNodeType() )
|
||||
return false;
|
||||
|
||||
m_Parent = aParent;
|
||||
|
||||
if( m_Parent )
|
||||
m_Parent->AddChildNode( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SGNODE* SGSHAPE::FindNode( const char* aNodeName, const SGNODE* aCaller )
|
||||
{
|
||||
if( nullptr == aNodeName || 0 == aNodeName[0] )
|
||||
return nullptr;
|
||||
|
||||
if( !m_Name.compare( aNodeName ) )
|
||||
return this;
|
||||
|
||||
SGNODE* tmp = nullptr;
|
||||
|
||||
if( nullptr != m_Appearance )
|
||||
{
|
||||
tmp = m_Appearance->FindNode( aNodeName, this );
|
||||
|
||||
if( tmp )
|
||||
{
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if( nullptr != m_FaceSet )
|
||||
{
|
||||
tmp = m_FaceSet->FindNode( aNodeName, this );
|
||||
|
||||
if( tmp )
|
||||
{
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// query the parent if appropriate
|
||||
if( aCaller == m_Parent || nullptr == m_Parent )
|
||||
return nullptr;
|
||||
|
||||
return m_Parent->FindNode( aNodeName, this );
|
||||
}
|
||||
|
||||
|
||||
void SGSHAPE::unlinkNode( const SGNODE* aNode, bool isChild )
|
||||
{
|
||||
if( nullptr == aNode )
|
||||
return;
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
if( aNode == m_Appearance )
|
||||
{
|
||||
m_Appearance = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_FaceSet )
|
||||
{
|
||||
m_FaceSet = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aNode == m_RAppearance )
|
||||
{
|
||||
delNodeRef( this );
|
||||
m_RAppearance = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if( aNode == m_RFaceSet )
|
||||
{
|
||||
delNodeRef( this );
|
||||
m_RFaceSet = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] unlinkNode() did not find its target" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
}
|
||||
|
||||
|
||||
void SGSHAPE::unlinkChildNode( const SGNODE* aNode )
|
||||
{
|
||||
unlinkNode( aNode, true );
|
||||
}
|
||||
|
||||
|
||||
void SGSHAPE::unlinkRefNode( const SGNODE* aNode )
|
||||
{
|
||||
unlinkNode( aNode, false );
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::addNode( SGNODE* aNode, bool isChild )
|
||||
{
|
||||
wxCHECK( aNode, false );
|
||||
|
||||
if( S3D::SGTYPE_APPEARANCE == aNode->GetNodeType() )
|
||||
{
|
||||
if( m_Appearance || m_RAppearance )
|
||||
{
|
||||
if( aNode != m_Appearance && aNode != m_RAppearance )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] assigning multiple Appearance "
|
||||
"nodes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
m_Appearance = (SGAPPEARANCE*)aNode;
|
||||
m_Appearance->SetParent( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RAppearance = (SGAPPEARANCE*)aNode;
|
||||
m_RAppearance->addNodeRef( this );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_FACESET == aNode->GetNodeType() )
|
||||
{
|
||||
if( m_FaceSet || m_RFaceSet )
|
||||
{
|
||||
if( aNode != m_FaceSet && aNode != m_RFaceSet )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] assigning multiple FaceSet nodes" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( isChild )
|
||||
{
|
||||
m_FaceSet = (SGFACESET*)aNode;
|
||||
m_FaceSet->SetParent( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RFaceSet = (SGFACESET*)aNode;
|
||||
m_RFaceSet->addNodeRef( this );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] object %s is not a valid type for this "
|
||||
"object (%d)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, aNode->GetName(), aNode->GetNodeType() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::AddRefNode( SGNODE* aNode )
|
||||
{
|
||||
return addNode( aNode, false );
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::AddChildNode( SGNODE* aNode )
|
||||
{
|
||||
return addNode( aNode, true );
|
||||
}
|
||||
|
||||
|
||||
void SGSHAPE::ReNameNodes( void )
|
||||
{
|
||||
m_written = false;
|
||||
|
||||
// rename this node
|
||||
m_Name.clear();
|
||||
GetName();
|
||||
|
||||
// rename Appearance
|
||||
if( m_Appearance )
|
||||
m_Appearance->ReNameNodes();
|
||||
|
||||
// rename FaceSet
|
||||
if( m_FaceSet )
|
||||
m_FaceSet->ReNameNodes();
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::WriteVRML( std::ostream& aFile, bool aReuseFlag )
|
||||
{
|
||||
if( !m_Appearance && !m_RAppearance && !m_FaceSet && !m_RFaceSet )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aReuseFlag )
|
||||
{
|
||||
if( !m_written )
|
||||
{
|
||||
aFile << "DEF " << GetName() << " Shape {\n";
|
||||
m_written = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " USE " << GetName() << "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aFile << " Shape {\n";
|
||||
}
|
||||
|
||||
if( m_Appearance )
|
||||
m_Appearance->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_RAppearance )
|
||||
m_RAppearance->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_FaceSet )
|
||||
m_FaceSet->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
if( m_RFaceSet )
|
||||
m_RFaceSet->WriteVRML( aFile, aReuseFlag );
|
||||
|
||||
aFile << "}\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::WriteCache( std::ostream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
if( nullptr == parentNode )
|
||||
{
|
||||
wxCHECK( m_Parent, false );
|
||||
|
||||
SGNODE* np = m_Parent;
|
||||
|
||||
while( nullptr != np->GetParent() )
|
||||
np = np->GetParent();
|
||||
|
||||
if( np->WriteCache( aFile, nullptr ) )
|
||||
{
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCHECK( parentNode == m_Parent, false );
|
||||
|
||||
if( !aFile.good() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [BUG] bad stream" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if any references are unwritten and swap parents if so
|
||||
if( nullptr != m_RAppearance && !m_RAppearance->isWritten() )
|
||||
m_RAppearance->SwapParent(this);
|
||||
|
||||
if( nullptr != m_RFaceSet && !m_RFaceSet->isWritten() )
|
||||
m_RFaceSet->SwapParent( this );
|
||||
|
||||
aFile << "[" << GetName() << "]";
|
||||
#define NITEMS 4
|
||||
bool items[NITEMS];
|
||||
int i;
|
||||
|
||||
for( i = 0; i < NITEMS; ++i )
|
||||
items[i] = 0;
|
||||
|
||||
i = 0;
|
||||
|
||||
if( nullptr != m_Appearance )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
|
||||
if( nullptr != m_RAppearance )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
|
||||
if( nullptr != m_FaceSet )
|
||||
items[i] = true;
|
||||
|
||||
++i;
|
||||
|
||||
if( nullptr != m_RFaceSet )
|
||||
items[i] = true;
|
||||
|
||||
for( int jj = 0; jj < NITEMS; ++jj )
|
||||
aFile.write( (char*)&items[jj], sizeof(bool) );
|
||||
|
||||
if( items[0] )
|
||||
m_Appearance->WriteCache( aFile, this );
|
||||
|
||||
if( items[1] )
|
||||
aFile << "[" << m_RAppearance->GetName() << "]";
|
||||
|
||||
if( items[2] )
|
||||
m_FaceSet->WriteCache( aFile, this );
|
||||
|
||||
if( items[3] )
|
||||
aFile << "[" << m_RFaceSet->GetName() << "]";
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
m_written = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::ReadCache( std::istream& aFile, SGNODE* parentNode )
|
||||
{
|
||||
wxCHECK( m_Appearance == nullptr && m_RAppearance == nullptr && m_FaceSet == nullptr &&
|
||||
m_RFaceSet == nullptr, false );
|
||||
|
||||
#define NITEMS 4
|
||||
bool items[NITEMS];
|
||||
|
||||
for( int i = 0; i < NITEMS; ++i )
|
||||
aFile.read( (char*)&items[i], sizeof(bool) );
|
||||
|
||||
if( ( items[0] && items[1] ) || ( items[2] && items[3] ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; multiple item definitions "
|
||||
"at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
|
||||
if( items[0] )
|
||||
{
|
||||
if( S3D::SGTYPE_APPEARANCE != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child appearance "
|
||||
"tag at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Appearance = new SGAPPEARANCE( this );
|
||||
m_Appearance->SetName( name.c_str() );
|
||||
|
||||
if( !m_Appearance->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data while reading appearance "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, name );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( items[1] )
|
||||
{
|
||||
if( S3D::SGTYPE_APPEARANCE != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref appearance tag "
|
||||
"at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* np = FindNode( name.c_str(), this );
|
||||
|
||||
if( !np )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: cannot find ref "
|
||||
"appearance '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_APPEARANCE != np->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: type is not "
|
||||
"SGAPPEARANCE '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RAppearance = (SGAPPEARANCE*)np;
|
||||
m_RAppearance->addNodeRef( this );
|
||||
}
|
||||
|
||||
if( items[2] )
|
||||
{
|
||||
if( S3D::SGTYPE_FACESET != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad child face set tag "
|
||||
"at position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_FaceSet = new SGFACESET( this );
|
||||
m_FaceSet->SetName( name.c_str() );
|
||||
|
||||
if( !m_FaceSet->ReadCache( aFile, this ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data while reading face set "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, name );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( items[3] )
|
||||
{
|
||||
if( S3D::SGTYPE_FACESET != S3D::ReadTag( aFile, name ) )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data; bad ref face set tag at "
|
||||
"position %ul" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
static_cast<unsigned long>( aFile.tellg() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SGNODE* np = FindNode( name.c_str(), this );
|
||||
|
||||
if( !np )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: cannot find ref face "
|
||||
"set '%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( S3D::SGTYPE_FACESET != np->GetNodeType() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] corrupt data: type is not SGFACESET "
|
||||
"'%s'" ),
|
||||
__FILE__, __FUNCTION__, __LINE__,
|
||||
name );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RFaceSet = (SGFACESET*)np;
|
||||
m_RFaceSet->addNodeRef( this );
|
||||
}
|
||||
|
||||
if( aFile.fail() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SGSHAPE::Prepare( const glm::dmat4* aTransform, S3D::MATLIST& materials,
|
||||
std::vector< SMESH >& meshes )
|
||||
{
|
||||
SMESH m;
|
||||
S3D::INIT_SMESH( m );
|
||||
|
||||
SGAPPEARANCE* pa = m_Appearance;
|
||||
SGFACESET* pf = m_FaceSet;
|
||||
|
||||
if( nullptr == pa )
|
||||
pa = m_RAppearance;
|
||||
|
||||
if( nullptr == pf )
|
||||
pf = m_RFaceSet;
|
||||
|
||||
// no face sets = nothing to render, which is valid though pointless
|
||||
if( nullptr == pf )
|
||||
return true;
|
||||
|
||||
if( !pf->validate() )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; inconsistent data" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( nullptr == pa )
|
||||
{
|
||||
m.m_MaterialIdx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int idx;
|
||||
|
||||
if( !S3D::GetMatIndex( materials, pa, idx ) )
|
||||
{
|
||||
m.m_MaterialIdx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m.m_MaterialIdx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
SGCOLORS* pc = pf->m_Colors;
|
||||
SGCOORDS* pv = pf->m_Coords;
|
||||
SGCOORDINDEX* vidx = pf->m_CoordIndices;
|
||||
SGNORMALS* pn = pf->m_Normals;
|
||||
|
||||
if( nullptr == pc )
|
||||
pc = pf->m_RColors;
|
||||
|
||||
if( nullptr == pv )
|
||||
pv = pf->m_RCoords;
|
||||
|
||||
if( nullptr == pn )
|
||||
pn = pf->m_RNormals;
|
||||
|
||||
// set the vertex points and indices
|
||||
size_t nCoords = 0;
|
||||
SGPOINT* pCoords = nullptr;
|
||||
pv->GetCoordsList( nCoords, pCoords );
|
||||
|
||||
size_t nColors = 0;
|
||||
SGCOLOR* pColors = nullptr;
|
||||
|
||||
if( pc )
|
||||
{
|
||||
// check the vertex colors
|
||||
pc->GetColorList( nColors, pColors );
|
||||
|
||||
if( nColors < nCoords )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; not enough colors per "
|
||||
"vertex (%ul vs %ul)" ),
|
||||
__FILE__, __FUNCTION__, __LINE__, static_cast<unsigned long>( nColors ),
|
||||
static_cast<unsigned long>( nCoords ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// set the vertex indices
|
||||
size_t nvidx = 0;
|
||||
int* lv = nullptr;
|
||||
vidx->GetIndices( nvidx, lv );
|
||||
|
||||
// note: reduce the vertex set to include only the referenced vertices
|
||||
std::vector< int > vertices; // store the list of temp vertex indices
|
||||
std::map< int, unsigned int > indexmap; // map temp vertex to true vertex
|
||||
std::map< int, unsigned int >::iterator mit;
|
||||
|
||||
for( unsigned int i = 0; i < nvidx; ++i )
|
||||
{
|
||||
mit = indexmap.find( lv[i] );
|
||||
|
||||
if( mit == indexmap.end() )
|
||||
{
|
||||
indexmap.emplace( lv[i], vertices.size() );
|
||||
vertices.push_back( lv[i] );
|
||||
}
|
||||
}
|
||||
|
||||
if( vertices.size() < 3 )
|
||||
{
|
||||
wxLogTrace( MASK_3D_SG, wxT( "%s:%s:%d * [INFO] bad model; not enough vertices" ),
|
||||
__FILE__, __FUNCTION__, __LINE__ );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// construct the final vertex/color list
|
||||
SFVEC3F* lColors = nullptr;
|
||||
SFVEC3F* lCoords = new SFVEC3F[ vertices.size() ];
|
||||
int ti;
|
||||
|
||||
if( pc )
|
||||
{
|
||||
lColors = new SFVEC3F[vertices.size()];
|
||||
m.m_Color = lColors;
|
||||
}
|
||||
|
||||
if( pc )
|
||||
{
|
||||
for( size_t i = 0; i < vertices.size(); ++i )
|
||||
{
|
||||
ti = vertices[i];
|
||||
glm::dvec4 pt( pCoords[ti].x, pCoords[ti].y, pCoords[ti].z, 1.0 );
|
||||
pt = (*aTransform) * pt;
|
||||
pColors[ti].GetColor( lColors[i].x, lColors[i].y, lColors[i].z );
|
||||
lCoords[i] = SFVEC3F( pt.x, pt.y, pt.z );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( size_t i = 0; i < vertices.size(); ++i )
|
||||
{
|
||||
ti = vertices[i];
|
||||
glm::dvec4 pt( pCoords[ti].x, pCoords[ti].y, pCoords[ti].z, 1.0 );
|
||||
pt = (*aTransform) * pt;
|
||||
lCoords[i] = SFVEC3F( pt.x, pt.y, pt.z );
|
||||
}
|
||||
}
|
||||
|
||||
m.m_VertexSize = (unsigned int) vertices.size();
|
||||
m.m_Positions = lCoords;
|
||||
unsigned int* lvidx = new unsigned int[ nvidx ];
|
||||
|
||||
for( unsigned int i = 0; i < nvidx; ++i )
|
||||
{
|
||||
mit = indexmap.find( lv[i] );
|
||||
lvidx[i] = mit->second;
|
||||
}
|
||||
|
||||
m.m_FaceIdxSize = (unsigned int )nvidx;
|
||||
m.m_FaceIdx = lvidx;
|
||||
|
||||
// set the per-vertex normals
|
||||
size_t nNorms = 0;
|
||||
SGVECTOR* pNorms = nullptr;
|
||||
double x, y, z;
|
||||
|
||||
pn->GetNormalList( nNorms, pNorms );
|
||||
SFVEC3F* lNorms = new SFVEC3F[ vertices.size() ];
|
||||
|
||||
for( size_t i = 0; i < vertices.size(); ++i )
|
||||
{
|
||||
ti = vertices[i];
|
||||
pNorms[ti].GetVector( x, y, z );
|
||||
glm::dvec4 pt( x, y, z, 0.0 );
|
||||
pt = (*aTransform) * pt;
|
||||
|
||||
lNorms[i] = SFVEC3F( pt.x, pt.y, pt.z );
|
||||
}
|
||||
|
||||
m.m_Normals = lNorms;
|
||||
meshes.push_back( m );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sg_shape.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SG_SHAPE_H
|
||||
#define SG_SHAPE_H
|
||||
|
||||
#include <vector>
|
||||
#include "3d_cache/sg/sg_node.h"
|
||||
|
||||
class SGAPPEARANCE;
|
||||
class SGFACESET;
|
||||
|
||||
/**
|
||||
* Define a complex 3D shape for a scenegraph object.
|
||||
*/
|
||||
class SGSHAPE : public SGNODE
|
||||
{
|
||||
public:
|
||||
SGSHAPE( SGNODE* aParent );
|
||||
virtual ~SGSHAPE();
|
||||
|
||||
virtual bool SetParent( SGNODE* aParent, bool notify = true ) override;
|
||||
|
||||
SGNODE* FindNode(const char* aNodeName, const SGNODE* aCaller) override;
|
||||
bool AddRefNode( SGNODE* aNode ) override;
|
||||
bool AddChildNode( SGNODE* aNode ) override;
|
||||
|
||||
void ReNameNodes( void ) override;
|
||||
bool WriteVRML( std::ostream& aFile, bool aReuseFlag ) override;
|
||||
|
||||
bool WriteCache( std::ostream& aFile, SGNODE* parentNode ) override;
|
||||
bool ReadCache( std::istream& aFile, SGNODE* parentNode ) override;
|
||||
|
||||
bool Prepare( const glm::dmat4* aTransform, S3D::MATLIST& materials,
|
||||
std::vector< SMESH >& meshes );
|
||||
|
||||
void unlinkChildNode( const SGNODE* aNode ) override;
|
||||
void unlinkRefNode( const SGNODE* aNode ) override;
|
||||
|
||||
private:
|
||||
void unlinkNode( const SGNODE* aNode, bool isChild );
|
||||
bool addNode( SGNODE* aNode, bool isChild );
|
||||
|
||||
public:
|
||||
// owned node
|
||||
SGAPPEARANCE* m_Appearance;
|
||||
SGFACESET* m_FaceSet;
|
||||
|
||||
// referenced nodes
|
||||
SGAPPEARANCE* m_RAppearance;
|
||||
SGFACESET* m_RFaceSet;
|
||||
};
|
||||
|
||||
/*
|
||||
p.107
|
||||
Shape {
|
||||
appearance NULL
|
||||
geometry NULL
|
||||
}
|
||||
*/
|
||||
|
||||
#endif // SG_SHAPE_H
|
||||
@@ -0,0 +1,672 @@
|
||||
/**
|
||||
* @file 3d_canvas.cpp
|
||||
*/
|
||||
#include <fctsys.h>
|
||||
#include <trigo.h>
|
||||
|
||||
#include <wx/image.h>
|
||||
|
||||
#if !wxUSE_GLCANVAS
|
||||
#error Please set wxUSE_GLCANVAS to 1 in setup.h.
|
||||
#endif
|
||||
|
||||
#include <wx/dataobj.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
#include <gestfich.h>
|
||||
|
||||
#include <3d_viewer.h>
|
||||
#include <3d_canvas.h>
|
||||
#include <info3d_visu.h>
|
||||
#include <trackball.h>
|
||||
#include <3d_viewer_id.h>
|
||||
|
||||
|
||||
// -----------------
|
||||
// helper function (from wxWidgets, opengl/cube.cpp sample
|
||||
// -----------------
|
||||
void CheckGLError()
|
||||
{
|
||||
GLenum errLast = GL_NO_ERROR;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
GLenum err = glGetError();
|
||||
if ( err == GL_NO_ERROR )
|
||||
return;
|
||||
|
||||
// normally the error is reset by the call to glGetError() but if
|
||||
// glGetError() itself returns an error, we risk looping forever here
|
||||
// so check that we get a different error than the last time
|
||||
if ( err == errLast )
|
||||
{
|
||||
wxLogError(wxT("OpenGL error state couldn't be reset."));
|
||||
return;
|
||||
}
|
||||
|
||||
errLast = err;
|
||||
|
||||
wxLogError(wxT("OpenGL error %d"), err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EDA_3D_CANVAS implementation
|
||||
*/
|
||||
|
||||
BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
|
||||
EVT_PAINT( EDA_3D_CANVAS::OnPaint )
|
||||
|
||||
// key event:
|
||||
EVT_CHAR( EDA_3D_CANVAS::OnChar )
|
||||
|
||||
// mouse events
|
||||
EVT_RIGHT_DOWN( EDA_3D_CANVAS::OnRightClick )
|
||||
EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
|
||||
EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
|
||||
|
||||
// other events
|
||||
EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
|
||||
EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START, ID_POPUP_3D_VIEW_END, EDA_3D_CANVAS::OnPopUpMenu )
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
EDA_3D_CANVAS::EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList ) :
|
||||
wxGLCanvas( parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
||||
wxFULL_REPAINT_ON_RESIZE )
|
||||
{
|
||||
m_init = false;
|
||||
m_gllist = 0;
|
||||
// Explicitly create a new rendering context instance for this canvas.
|
||||
m_glRC = new wxGLContext( this );
|
||||
|
||||
DisplayStatus();
|
||||
}
|
||||
|
||||
|
||||
EDA_3D_CANVAS::~EDA_3D_CANVAS()
|
||||
{
|
||||
ClearLists();
|
||||
m_init = false;
|
||||
delete m_glRC;
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::ClearLists()
|
||||
{
|
||||
if( m_gllist > 0 )
|
||||
glDeleteLists( m_gllist, 1 );
|
||||
|
||||
m_gllist = 0;
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::OnChar( wxKeyEvent& event )
|
||||
{
|
||||
SetView3D( event.GetKeyCode() );
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::SetView3D( int keycode )
|
||||
{
|
||||
int ii;
|
||||
double delta_move = 0.7 * g_Parm_3D_Visu.m_Zoom;
|
||||
|
||||
switch( keycode )
|
||||
{
|
||||
case WXK_LEFT:
|
||||
m_draw3dOffset.x -= delta_move;
|
||||
break;
|
||||
|
||||
case WXK_RIGHT:
|
||||
m_draw3dOffset.x += delta_move;
|
||||
break;
|
||||
|
||||
case WXK_UP:
|
||||
m_draw3dOffset.y += delta_move;
|
||||
break;
|
||||
|
||||
case WXK_DOWN:
|
||||
m_draw3dOffset.y -= delta_move;
|
||||
break;
|
||||
|
||||
case WXK_HOME:
|
||||
g_Parm_3D_Visu.m_Zoom = 1.0;
|
||||
m_draw3dOffset.x = m_draw3dOffset.y = 0;
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
break;
|
||||
|
||||
case WXK_END:
|
||||
break;
|
||||
|
||||
case WXK_F1:
|
||||
g_Parm_3D_Visu.m_Zoom /= 1.4;
|
||||
if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
|
||||
g_Parm_3D_Visu.m_Zoom = 0.01;
|
||||
break;
|
||||
|
||||
case WXK_F2:
|
||||
g_Parm_3D_Visu.m_Zoom *= 1.4;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
break;
|
||||
|
||||
case '-':
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
case 'R':
|
||||
m_draw3dOffset.x = m_draw3dOffset.y = 0;
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
g_Parm_3D_Visu.m_ROTZ = -90;
|
||||
g_Parm_3D_Visu.m_ROTX = -90;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
g_Parm_3D_Visu.m_ROTZ = 90;
|
||||
g_Parm_3D_Visu.m_ROTX = -90;
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
g_Parm_3D_Visu.m_ROTX = -90;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
g_Parm_3D_Visu.m_ROTX = -90;
|
||||
g_Parm_3D_Visu.m_ROTZ = -180;
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
g_Parm_3D_Visu.m_ROTX = -180;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayStatus();
|
||||
Refresh( false );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
|
||||
{
|
||||
if( event.ShiftDown() )
|
||||
{
|
||||
if( event.GetWheelRotation() < 0 )
|
||||
{
|
||||
/* up */
|
||||
SetView3D( WXK_UP );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* down */
|
||||
SetView3D( WXK_DOWN );
|
||||
}
|
||||
}
|
||||
else if( event.ControlDown() )
|
||||
{
|
||||
if( event.GetWheelRotation() > 0 )
|
||||
{
|
||||
/* right */
|
||||
SetView3D( WXK_RIGHT );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* left */
|
||||
SetView3D( WXK_LEFT );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( event.GetWheelRotation() > 0 )
|
||||
{
|
||||
g_Parm_3D_Visu.m_Zoom /= 1.4;
|
||||
|
||||
if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
|
||||
g_Parm_3D_Visu.m_Zoom = 0.01;
|
||||
}
|
||||
else
|
||||
g_Parm_3D_Visu.m_Zoom *= 1.4;
|
||||
|
||||
DisplayStatus();
|
||||
Refresh( false );
|
||||
}
|
||||
|
||||
g_Parm_3D_Visu.m_Beginx = event.GetX();
|
||||
g_Parm_3D_Visu.m_Beginy = event.GetY();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
|
||||
{
|
||||
wxSize size( GetClientSize() );
|
||||
double spin_quat[4];
|
||||
|
||||
if( event.Dragging() )
|
||||
{
|
||||
if( event.LeftIsDown() )
|
||||
{
|
||||
/* drag in progress, simulate trackball */
|
||||
trackball( spin_quat,
|
||||
(2.0 * g_Parm_3D_Visu.m_Beginx - size.x) / size.x,
|
||||
(size.y - 2.0 * g_Parm_3D_Visu.m_Beginy) / size.y,
|
||||
( 2.0 * event.GetX() - size.x) / size.x,
|
||||
( size.y - 2.0 * event.GetY() ) / size.y );
|
||||
|
||||
add_quats( spin_quat, g_Parm_3D_Visu.m_Quat, g_Parm_3D_Visu.m_Quat );
|
||||
}
|
||||
else if( event.MiddleIsDown() )
|
||||
{
|
||||
/* middle button drag -> pan */
|
||||
|
||||
/* Current zoom and an additional factor are taken into account
|
||||
* for the amount of panning. */
|
||||
const double PAN_FACTOR = 8.0 * g_Parm_3D_Visu.m_Zoom;
|
||||
m_draw3dOffset.x -= PAN_FACTOR *
|
||||
( g_Parm_3D_Visu.m_Beginx - event.GetX() ) / size.x;
|
||||
m_draw3dOffset.y -= PAN_FACTOR *
|
||||
(event.GetY() - g_Parm_3D_Visu.m_Beginy) / size.y;
|
||||
}
|
||||
|
||||
/* orientation has changed, redraw mesh */
|
||||
DisplayStatus();
|
||||
Refresh( false );
|
||||
}
|
||||
|
||||
g_Parm_3D_Visu.m_Beginx = event.GetX();
|
||||
g_Parm_3D_Visu.m_Beginy = event.GetY();
|
||||
}
|
||||
|
||||
|
||||
/* Construct and display a popup menu when the right button is clicked.
|
||||
*/
|
||||
void EDA_3D_CANVAS::OnRightClick( wxMouseEvent& event )
|
||||
{
|
||||
wxPoint pos;
|
||||
wxMenu PopUpMenu;
|
||||
|
||||
pos.x = event.GetX();
|
||||
pos.y = event.GetY();
|
||||
|
||||
wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN, _( "Zoom +" ) );
|
||||
item->SetBitmap( KiBitmap( zoom_in_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT, _( "Zoom -" ) );
|
||||
item->SetBitmap( KiBitmap( zoom_out_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
PopUpMenu.AppendSeparator();
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS, _( "Top View" ) );
|
||||
item->SetBitmap( KiBitmap( axis3d_top_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG, _( "Bottom View" ) );
|
||||
item->SetBitmap( KiBitmap( axis3d_bottom_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
PopUpMenu.AppendSeparator();
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS, _( "Right View" ) );
|
||||
item->SetBitmap( KiBitmap( axis3d_right_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG, _( "Left View" ) );
|
||||
item->SetBitmap( KiBitmap( axis3d_left_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
PopUpMenu.AppendSeparator();
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS, _( "Front View" ) );
|
||||
item->SetBitmap( KiBitmap( axis3d_front_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG, _( "Back View" ) );
|
||||
item->SetBitmap( KiBitmap( axis3d_back_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
PopUpMenu.AppendSeparator();
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT, _( "Move left <-" ) );
|
||||
item->SetBitmap( KiBitmap( left_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT, _( "Move right ->" ) );
|
||||
item->SetBitmap( KiBitmap( right_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP, _( "Move Up ^" ) );
|
||||
item->SetBitmap( KiBitmap( up_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN, _( "Move Down" ) );
|
||||
item->SetBitmap( KiBitmap( down_xpm ));
|
||||
PopUpMenu.Append( item );
|
||||
|
||||
PopupMenu( &PopUpMenu, pos );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::OnPopUpMenu( wxCommandEvent& event )
|
||||
{
|
||||
int key = 0;
|
||||
|
||||
switch( event.GetId() )
|
||||
{
|
||||
case ID_POPUP_ZOOMIN:
|
||||
key = WXK_F1;
|
||||
break;
|
||||
|
||||
case ID_POPUP_ZOOMOUT:
|
||||
key = WXK_F2;
|
||||
break;
|
||||
|
||||
case ID_POPUP_VIEW_XPOS:
|
||||
key = 'x';
|
||||
break;
|
||||
|
||||
case ID_POPUP_VIEW_XNEG:
|
||||
key = 'X';
|
||||
break;
|
||||
|
||||
case ID_POPUP_VIEW_YPOS:
|
||||
key = 'y';
|
||||
break;
|
||||
|
||||
case ID_POPUP_VIEW_YNEG:
|
||||
key = 'Y';
|
||||
break;
|
||||
|
||||
case ID_POPUP_VIEW_ZPOS:
|
||||
key = 'z';
|
||||
break;
|
||||
|
||||
case ID_POPUP_VIEW_ZNEG:
|
||||
key = 'Z';
|
||||
break;
|
||||
|
||||
case ID_POPUP_MOVE3D_LEFT:
|
||||
key = WXK_LEFT;
|
||||
break;
|
||||
|
||||
case ID_POPUP_MOVE3D_RIGHT:
|
||||
key = WXK_RIGHT;
|
||||
break;
|
||||
|
||||
case ID_POPUP_MOVE3D_UP:
|
||||
key = WXK_UP;
|
||||
break;
|
||||
|
||||
case ID_POPUP_MOVE3D_DOWN:
|
||||
key = WXK_DOWN;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
SetView3D( key );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::DisplayStatus()
|
||||
{
|
||||
wxString msg;
|
||||
|
||||
msg.Printf( wxT( "dx %3.2f" ), m_draw3dOffset.x );
|
||||
Parent()->SetStatusText( msg, 1 );
|
||||
|
||||
msg.Printf( wxT( "dy %3.2f" ), m_draw3dOffset.y );
|
||||
Parent()->SetStatusText( msg, 2 );
|
||||
|
||||
msg.Printf( wxT( "View: %3.1f" ), 45 * g_Parm_3D_Visu.m_Zoom );
|
||||
Parent()->SetStatusText( msg, 3 );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::OnPaint( wxPaintEvent& event )
|
||||
{
|
||||
wxPaintDC dc( this );
|
||||
|
||||
Redraw();
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
|
||||
{
|
||||
// Do nothing, to avoid flashing.
|
||||
}
|
||||
|
||||
|
||||
/* Initialize broad parameters for OpenGL */
|
||||
void EDA_3D_CANVAS::InitGL()
|
||||
{
|
||||
wxSize size = GetClientSize();
|
||||
|
||||
if( !m_init )
|
||||
{
|
||||
m_init = true;
|
||||
g_Parm_3D_Visu.m_Zoom = 1.0;
|
||||
m_ZBottom = 1.0;
|
||||
m_ZTop = 10.0;
|
||||
|
||||
glDisable( GL_CULL_FACE ); // show back faces
|
||||
glEnable( GL_DEPTH_TEST ); // Enable z-buferring
|
||||
glEnable( GL_ALPHA_TEST );
|
||||
glEnable( GL_LINE_SMOOTH );
|
||||
// glEnable(GL_POLYGON_SMOOTH); // creates issues with some graphic cards
|
||||
glShadeModel( GL_SMOOTH );
|
||||
glEnable( GL_COLOR_MATERIAL );
|
||||
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
||||
|
||||
// speedups
|
||||
glEnable( GL_DITHER );
|
||||
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE );
|
||||
glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
|
||||
glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
|
||||
|
||||
// Initialize alpha blending function.
|
||||
glEnable( GL_BLEND );
|
||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
}
|
||||
|
||||
// set viewing projection
|
||||
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadIdentity();
|
||||
|
||||
#define MAX_VIEW_ANGLE 160.0 / 45.0
|
||||
if( g_Parm_3D_Visu.m_Zoom > MAX_VIEW_ANGLE )
|
||||
g_Parm_3D_Visu.m_Zoom = MAX_VIEW_ANGLE;
|
||||
|
||||
if( Parent()->ModeIsOrtho() )
|
||||
{
|
||||
// OrthoReductionFactor is chosen so as to provide roughly the same size as
|
||||
// Perspective View
|
||||
const double orthoReductionFactor = 400 / g_Parm_3D_Visu.m_Zoom;
|
||||
|
||||
// Initialize Projection Matrix for Ortographic View
|
||||
glOrtho( -size.x / orthoReductionFactor, size.x / orthoReductionFactor,
|
||||
-size.y / orthoReductionFactor, size.y / orthoReductionFactor, 1, 10 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ratio width / height of the window display
|
||||
double ratio_HV = (double) size.x / size.y;
|
||||
|
||||
// Initialize Projection Matrix for Perspective View
|
||||
gluPerspective( 45.0 * g_Parm_3D_Visu.m_Zoom, ratio_HV, 1, 10 );
|
||||
}
|
||||
|
||||
|
||||
// position viewer
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
glTranslatef( 0.0F, 0.0F, -( m_ZBottom + m_ZTop) / 2 );
|
||||
|
||||
// clear color and depth buffers
|
||||
glClearColor( g_Parm_3D_Visu.m_BgColor.m_Red,
|
||||
g_Parm_3D_Visu.m_BgColor.m_Green,
|
||||
g_Parm_3D_Visu.m_BgColor.m_Blue, 1 );
|
||||
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||
|
||||
// Setup light sources:
|
||||
SetLights();
|
||||
|
||||
CheckGLError();
|
||||
}
|
||||
|
||||
|
||||
/* Initialize OpenGL light sources. */
|
||||
void EDA_3D_CANVAS::SetLights()
|
||||
{
|
||||
double light;
|
||||
GLfloat light_color[4];
|
||||
|
||||
/* set viewing projection */
|
||||
light_color[3] = 1.0;
|
||||
GLfloat Z_axis_pos[4] = { 0.0, 0.0, 3.0, 0.0 };
|
||||
GLfloat lowZ_axis_pos[4] = { 0.0, 0.0, -3.0, 0.5 };
|
||||
|
||||
/* activate light */
|
||||
light = 1.0;
|
||||
light_color[0] = light_color[1] = light_color[2] = light;
|
||||
glLightfv( GL_LIGHT0, GL_POSITION, Z_axis_pos );
|
||||
glLightfv( GL_LIGHT0, GL_DIFFUSE, light_color );
|
||||
light = 0.3;
|
||||
light_color[0] = light_color[1] = light_color[2] = light;
|
||||
glLightfv( GL_LIGHT1, GL_POSITION, lowZ_axis_pos );
|
||||
glLightfv( GL_LIGHT1, GL_DIFFUSE, light_color );
|
||||
glEnable( GL_LIGHT0 ); // White spot on Z axis
|
||||
glEnable( GL_LIGHT1 ); // White spot on Z axis ( bottom)
|
||||
glEnable( GL_LIGHTING );
|
||||
}
|
||||
|
||||
|
||||
/* Create a Screenshot of the current 3D view.
|
||||
* Output file format is png or jpeg, or image is copied to the clipboard
|
||||
*/
|
||||
void EDA_3D_CANVAS::TakeScreenshot( wxCommandEvent& event )
|
||||
{
|
||||
wxFileName fn( Parent()->GetDefaultFileName() );
|
||||
wxString FullFileName;
|
||||
wxString file_ext, mask;
|
||||
bool fmt_is_jpeg = false;
|
||||
|
||||
if( event.GetId() == ID_MENU_SCREENCOPY_JPEG )
|
||||
fmt_is_jpeg = true;
|
||||
|
||||
if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD )
|
||||
{
|
||||
file_ext = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png" );
|
||||
mask = wxT( "*." ) + file_ext;
|
||||
FullFileName = Parent()->GetDefaultFileName();
|
||||
fn.SetExt( file_ext );
|
||||
|
||||
FullFileName = EDA_FileSelector( _( "3D Image filename:" ), wxEmptyString,
|
||||
fn.GetFullName(), file_ext, mask, this,
|
||||
wxFD_SAVE, true );
|
||||
|
||||
if( FullFileName.IsEmpty() )
|
||||
return;
|
||||
|
||||
// Be sure the screen area destroyed by the file dialog is redrawn before making
|
||||
// a screen copy.
|
||||
// Without this call, under Linux the screen refresh is made to late.
|
||||
wxYield();
|
||||
}
|
||||
|
||||
struct vieport_params
|
||||
{
|
||||
GLint originx;
|
||||
GLint originy;
|
||||
GLint x;
|
||||
GLint y;
|
||||
} viewport;
|
||||
|
||||
// Build image from the 3D buffer
|
||||
wxWindowUpdateLocker noUpdates( this );
|
||||
glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport );
|
||||
|
||||
unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 );
|
||||
unsigned char* alphabuffer = (unsigned char*) malloc( viewport.x * viewport.y );
|
||||
wxImage image( viewport.x, viewport.y );
|
||||
|
||||
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
|
||||
glReadBuffer( GL_BACK_LEFT );
|
||||
glReadPixels( viewport.originx, viewport.originy,
|
||||
viewport.x, viewport.y,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, pixelbuffer );
|
||||
glReadPixels( viewport.originx, viewport.originy,
|
||||
viewport.x, viewport.y,
|
||||
GL_ALPHA, GL_UNSIGNED_BYTE, alphabuffer );
|
||||
|
||||
|
||||
image.SetData( pixelbuffer );
|
||||
image.SetAlpha( alphabuffer );
|
||||
image = image.Mirror( false );
|
||||
wxBitmap bitmap( image );
|
||||
|
||||
if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD )
|
||||
{
|
||||
wxBitmapDataObject* dobjBmp = new wxBitmapDataObject;
|
||||
dobjBmp->SetBitmap( bitmap );
|
||||
|
||||
if( wxTheClipboard->Open() )
|
||||
{
|
||||
if( !wxTheClipboard->SetData( dobjBmp ) )
|
||||
wxMessageBox( _( "Failed to copy image to clipboard" ) );
|
||||
|
||||
wxTheClipboard->Flush(); /* the data in clipboard will stay
|
||||
* available after the
|
||||
* application exits */
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxImage image = bitmap.ConvertToImage();
|
||||
|
||||
if( !image.SaveFile( FullFileName,
|
||||
fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
|
||||
wxMessageBox( _( "Can't save file" ) );
|
||||
|
||||
image.Destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
|
||||
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_viewer.h
|
||||
*/
|
||||
|
||||
#ifndef _3D_CANVAS_H_
|
||||
#define _3D_CANVAS_H_
|
||||
|
||||
#include <wx/glcanvas.h>
|
||||
|
||||
#ifdef __WXMAC__
|
||||
# ifdef __DARWIN__
|
||||
# include <OpenGL/glu.h>
|
||||
# else
|
||||
# include <glu.h>
|
||||
# endif
|
||||
#else
|
||||
# include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include <3d_struct.h>
|
||||
|
||||
class BOARD_DESIGN_SETTINGS;
|
||||
class EDA_3D_FRAME;
|
||||
class S3D_VERTEX;
|
||||
class SEGVIA;
|
||||
class D_PAD;
|
||||
|
||||
|
||||
class EDA_3D_CANVAS : public wxGLCanvas
|
||||
{
|
||||
private:
|
||||
bool m_init;
|
||||
GLuint m_gllist;
|
||||
wxGLContext* m_glRC;
|
||||
wxRealPoint m_draw3dOffset; // offset to draw the 3 mesh.
|
||||
double m_ZBottom; // position of the back layer
|
||||
double m_ZTop; // position of the front layer
|
||||
|
||||
public:
|
||||
EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList = 0 );
|
||||
~EDA_3D_CANVAS();
|
||||
|
||||
EDA_3D_FRAME* Parent() { return (EDA_3D_FRAME*)GetParent(); }
|
||||
|
||||
void ClearLists();
|
||||
|
||||
// Event functions:
|
||||
void OnPaint( wxPaintEvent& event );
|
||||
void OnEraseBackground( wxEraseEvent& event );
|
||||
void OnChar( wxKeyEvent& event );
|
||||
void OnMouseWheel( wxMouseEvent& event );
|
||||
void OnMouseMove( wxMouseEvent& event );
|
||||
void OnRightClick( wxMouseEvent& event );
|
||||
void OnPopUpMenu( wxCommandEvent& event );
|
||||
void TakeScreenshot( wxCommandEvent& event );
|
||||
void OnEnterWindow( wxMouseEvent& event );
|
||||
|
||||
// Display functions
|
||||
GLuint DisplayCubeforTest(); // Just a test function
|
||||
void SetView3D( int keycode );
|
||||
void DisplayStatus();
|
||||
void Redraw( bool finish = false );
|
||||
void Render();
|
||||
|
||||
/**
|
||||
* Function CreateDrawGL_List
|
||||
* Prepares the parameters of the OpenGL draw list
|
||||
* creates the OpenGL draw list items (board, grid ...
|
||||
*/
|
||||
GLuint CreateDrawGL_List();
|
||||
void InitGL();
|
||||
void SetLights();
|
||||
void SetOffset(double aPosX, double aPosY)
|
||||
{
|
||||
m_draw3dOffset.x = aPosX;
|
||||
m_draw3dOffset.y = aPosY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function BuildBoard3DView
|
||||
* Called by CreateDrawGL_List()
|
||||
* Fills the OpenGL draw list with board items draw list.
|
||||
*/
|
||||
void BuildBoard3DView();
|
||||
|
||||
void DrawGrid( double aGriSizeMM );
|
||||
void Draw3DViaHole( SEGVIA * aVia );
|
||||
void Draw3DPadHole( D_PAD * aPad );
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
#endif /* _3D_CANVAS_H_ */
|
||||
@@ -1,752 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2022 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "../3d_rendering/camera.h"
|
||||
#include "board_adapter.h"
|
||||
#include <board_design_settings.h>
|
||||
#include <board_stackup_manager/board_stackup.h>
|
||||
#include <board_stackup_manager/stackup_predefined_prms.h>
|
||||
#include <3d_rendering/raytracing/shapes2D/polygon_2d.h>
|
||||
#include <board.h>
|
||||
#include <dialogs/dialog_color_picker.h>
|
||||
#include <3d_math.h>
|
||||
#include "3d_fastmath.h"
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <pgm_base.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <wx/log.h>
|
||||
#include <advanced_config.h>
|
||||
|
||||
|
||||
#define DEFAULT_BOARD_THICKNESS pcbIUScale.mmToIU( 1.6 )
|
||||
#define DEFAULT_COPPER_THICKNESS pcbIUScale.mmToIU( 0.035 ) // for 35 um
|
||||
// The solder mask layer (and silkscreen) thickness
|
||||
#define DEFAULT_TECH_LAYER_THICKNESS pcbIUScale.mmToIU( 0.025 )
|
||||
// The solder paste thickness is chosen bigger than the solder mask layer
|
||||
// to be sure is covers the mask when overlapping.
|
||||
#define SOLDERPASTE_LAYER_THICKNESS pcbIUScale.mmToIU( 0.04 )
|
||||
|
||||
|
||||
CUSTOM_COLORS_LIST BOARD_ADAPTER::g_SilkscreenColors;
|
||||
CUSTOM_COLORS_LIST BOARD_ADAPTER::g_MaskColors;
|
||||
CUSTOM_COLORS_LIST BOARD_ADAPTER::g_PasteColors;
|
||||
CUSTOM_COLORS_LIST BOARD_ADAPTER::g_FinishColors;
|
||||
CUSTOM_COLORS_LIST BOARD_ADAPTER::g_BoardColors;
|
||||
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBackgroundTop;
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBackgroundBot;
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSilkscreen;
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSolderMask;
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSolderPaste;
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSurfaceFinish;
|
||||
KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBoardBody;
|
||||
|
||||
// To be used in Raytracing render to create bevels on layer items
|
||||
float g_BevelThickness3DU = 0.0f;
|
||||
|
||||
static bool g_ColorsLoaded = false;
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable the trace output of this class.
|
||||
* The debug output can be turned on by setting the WXTRACE environment variable to
|
||||
* "KI_TRACE_EDA_CINFO3D_VISU". See the wxWidgets documentation on wxLogTrace for
|
||||
* more information.
|
||||
*
|
||||
* @ingroup trace_env_vars
|
||||
*/
|
||||
const wxChar *BOARD_ADAPTER::m_logTrace = wxT( "KI_TRACE_EDA_CINFO3D_VISU" );
|
||||
|
||||
|
||||
BOARD_ADAPTER::BOARD_ADAPTER() :
|
||||
m_Cfg( nullptr ),
|
||||
m_IsBoardView( true ),
|
||||
m_MousewheelPanning( true ),
|
||||
m_IsPreviewer( false ),
|
||||
m_board( nullptr ),
|
||||
m_3dModelManager( nullptr ),
|
||||
m_renderSettings( nullptr ),
|
||||
m_colors( nullptr ),
|
||||
m_layerZcoordTop(),
|
||||
m_layerZcoordBottom()
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::BOARD_ADAPTER" ) );
|
||||
|
||||
if( PgmOrNull() )
|
||||
m_colors = Pgm().GetSettingsManager().GetColorSettings();
|
||||
|
||||
m_boardPos = VECTOR2I();
|
||||
m_boardSize = VECTOR2I();
|
||||
m_boardCenter = SFVEC3F( 0.0f );
|
||||
|
||||
m_boardBoundingBox.Reset();
|
||||
|
||||
m_throughHoleIds.Clear();
|
||||
m_throughHoleOds.Clear();
|
||||
m_throughHoleAnnularRings.Clear();
|
||||
|
||||
m_copperLayersCount = 2;
|
||||
|
||||
m_biuTo3Dunits = 1.0;
|
||||
m_boardBodyThickness3DU = DEFAULT_BOARD_THICKNESS * m_biuTo3Dunits;
|
||||
m_frontCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
|
||||
m_backCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
|
||||
m_nonCopperLayerThickness3DU = DEFAULT_TECH_LAYER_THICKNESS * m_biuTo3Dunits;
|
||||
m_solderPasteLayerThickness3DU = SOLDERPASTE_LAYER_THICKNESS * m_biuTo3Dunits;
|
||||
|
||||
m_trackCount = 0;
|
||||
m_viaCount = 0;
|
||||
m_averageViaHoleDiameter = 0.0f;
|
||||
m_holeCount = 0;
|
||||
m_averageHoleDiameter = 0.0f;
|
||||
m_averageTrackWidth = 0.0f;
|
||||
|
||||
m_BgColorBot = SFVEC4F( 0.4, 0.4, 0.5, 1.0 );
|
||||
m_BgColorTop = SFVEC4F( 0.8, 0.8, 0.9, 1.0 );
|
||||
m_BoardBodyColor = SFVEC4F( 0.4, 0.4, 0.5, 0.9 );
|
||||
m_SolderMaskColorTop = SFVEC4F( 0.1, 0.2, 0.1, 0.83 );
|
||||
m_SolderMaskColorBot = SFVEC4F( 0.1, 0.2, 0.1, 0.83 );
|
||||
m_SolderPasteColor = SFVEC4F( 0.4, 0.4, 0.4, 1.0 );
|
||||
m_SilkScreenColorTop = SFVEC4F( 0.9, 0.9, 0.9, 1.0 );
|
||||
m_SilkScreenColorBot = SFVEC4F( 0.9, 0.9, 0.9, 1.0 );
|
||||
m_CopperColor = SFVEC4F( 0.75, 0.61, 0.23, 1.0 );
|
||||
|
||||
m_platedPadsFront = nullptr;
|
||||
m_platedPadsBack = nullptr;
|
||||
|
||||
m_frontPlatedPadPolys = nullptr;
|
||||
m_backPlatedPadPolys = nullptr;
|
||||
|
||||
if( !g_ColorsLoaded )
|
||||
{
|
||||
#define ADD_COLOR( list, r, g, b, a, name ) \
|
||||
list.emplace_back( r/255.0, g/255.0, b/255.0, a, name )
|
||||
|
||||
ADD_COLOR( g_SilkscreenColors, 245, 245, 245, 1.0, NotSpecifiedPrm() ); // White
|
||||
ADD_COLOR( g_SilkscreenColors, 20, 51, 36, 1.0, wxT( "Green" ) );
|
||||
ADD_COLOR( g_SilkscreenColors, 181, 19, 21, 1.0, wxT( "Red" ) );
|
||||
ADD_COLOR( g_SilkscreenColors, 2, 59, 162, 1.0, wxT( "Blue" ) );
|
||||
ADD_COLOR( g_SilkscreenColors, 11, 11, 11, 1.0, wxT( "Black" ) );
|
||||
ADD_COLOR( g_SilkscreenColors, 245, 245, 245, 1.0, wxT( "White" ) );
|
||||
ADD_COLOR( g_SilkscreenColors, 32, 2, 53, 1.0, wxT( "Purple" ) );
|
||||
ADD_COLOR( g_SilkscreenColors, 194, 195, 0, 1.0, wxT( "Yellow" ) );
|
||||
|
||||
ADD_COLOR( g_MaskColors, 20, 51, 36, 0.83, NotSpecifiedPrm() ); // Green
|
||||
ADD_COLOR( g_MaskColors, 20, 51, 36, 0.83, wxT( "Green" ) );
|
||||
ADD_COLOR( g_MaskColors, 91, 168, 12, 0.83, wxT( "Light Green" ) );
|
||||
ADD_COLOR( g_MaskColors, 13, 104, 11, 0.83, wxT( "Saturated Green" ) );
|
||||
ADD_COLOR( g_MaskColors, 181, 19, 21, 0.83, wxT( "Red" ) );
|
||||
ADD_COLOR( g_MaskColors, 210, 40, 14, 0.83, wxT( "Light Red" ) );
|
||||
ADD_COLOR( g_MaskColors, 239, 53, 41, 0.83, wxT( "Red/Orange" ) );
|
||||
ADD_COLOR( g_MaskColors, 2, 59, 162, 0.83, wxT( "Blue" ) );
|
||||
ADD_COLOR( g_MaskColors, 54, 79, 116, 0.83, wxT( "Light Blue 1" ) );
|
||||
ADD_COLOR( g_MaskColors, 61, 85, 130, 0.83, wxT( "Light Blue 2" ) );
|
||||
ADD_COLOR( g_MaskColors, 21, 70, 80, 0.83, wxT( "Green/Blue" ) );
|
||||
ADD_COLOR( g_MaskColors, 11, 11, 11, 0.83, wxT( "Black" ) );
|
||||
ADD_COLOR( g_MaskColors, 245, 245, 245, 0.83, wxT( "White" ) );
|
||||
ADD_COLOR( g_MaskColors, 32, 2, 53, 0.83, wxT( "Purple" ) );
|
||||
ADD_COLOR( g_MaskColors, 119, 31, 91, 0.83, wxT( "Light Purple" ) );
|
||||
ADD_COLOR( g_MaskColors, 194, 195, 0, 0.83, wxT( "Yellow" ) );
|
||||
|
||||
ADD_COLOR( g_PasteColors, 128, 128, 128, 1.0, wxT( "Grey" ) );
|
||||
ADD_COLOR( g_PasteColors, 90, 90, 90, 1.0, wxT( "Dark Grey" ) );
|
||||
ADD_COLOR( g_PasteColors, 213, 213, 213, 1.0, wxT( "Silver" ) );
|
||||
|
||||
ADD_COLOR( g_FinishColors, 184, 115, 50, 1.0, wxT( "Copper" ) );
|
||||
ADD_COLOR( g_FinishColors, 178, 156, 0, 1.0, wxT( "Gold" ) );
|
||||
ADD_COLOR( g_FinishColors, 213, 213, 213, 1.0, wxT( "Silver" ) );
|
||||
ADD_COLOR( g_FinishColors, 160, 160, 160, 1.0, wxT( "Tin" ) );
|
||||
|
||||
ADD_COLOR( g_BoardColors, 51, 43, 22, 0.83, wxT( "FR4 natural, dark" ) );
|
||||
ADD_COLOR( g_BoardColors, 109, 116, 75, 0.83, wxT( "FR4 natural" ) );
|
||||
ADD_COLOR( g_BoardColors, 252, 252, 250, 0.90, wxT( "PTFE natural" ) );
|
||||
ADD_COLOR( g_BoardColors, 205, 130, 0, 0.68, wxT( "Polyimide" ) );
|
||||
ADD_COLOR( g_BoardColors, 92, 17, 6, 0.90, wxT( "Phenolic natural" ) );
|
||||
ADD_COLOR( g_BoardColors, 146, 99, 47, 0.83, wxT( "Brown 1" ) );
|
||||
ADD_COLOR( g_BoardColors, 160, 123, 54, 0.83, wxT( "Brown 2" ) );
|
||||
ADD_COLOR( g_BoardColors, 146, 99, 47, 0.83, wxT( "Brown 3" ) );
|
||||
ADD_COLOR( g_BoardColors, 213, 213, 213, 1.0, wxT( "Aluminum" ) );
|
||||
|
||||
g_DefaultBackgroundTop = COLOR4D( 0.80, 0.80, 0.90, 1.0 );
|
||||
g_DefaultBackgroundBot = COLOR4D( 0.40, 0.40, 0.50, 1.0 );
|
||||
|
||||
g_DefaultSilkscreen = COLOR4D( 0.94, 0.94, 0.94, 1.0 );
|
||||
g_DefaultSolderMask = COLOR4D( 0.08, 0.20, 0.14, 0.83 );
|
||||
g_DefaultSolderPaste = COLOR4D( 0.50, 0.50, 0.50, 1.0 );
|
||||
g_DefaultSurfaceFinish = COLOR4D( 0.75, 0.61, 0.23, 1.0 );
|
||||
g_DefaultBoardBody = COLOR4D( 0.43, 0.45, 0.30, 0.90 );
|
||||
|
||||
g_ColorsLoaded = true;
|
||||
}
|
||||
#undef ADD_COLOR
|
||||
}
|
||||
|
||||
|
||||
BOARD_ADAPTER::~BOARD_ADAPTER()
|
||||
{
|
||||
destroyLayers();
|
||||
}
|
||||
|
||||
|
||||
bool BOARD_ADAPTER::Is3dLayerEnabled( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
wxASSERT( aLayer < PCB_LAYER_ID_COUNT );
|
||||
|
||||
if( m_board && !m_board->IsLayerEnabled( aLayer ) )
|
||||
return false;
|
||||
|
||||
// see if layer needs to be shown
|
||||
// check the flags
|
||||
switch( aLayer )
|
||||
{
|
||||
case B_Adhes:
|
||||
case F_Adhes:
|
||||
return m_Cfg->m_Render.show_adhesive;
|
||||
|
||||
case B_Paste:
|
||||
case F_Paste:
|
||||
return m_Cfg->m_Render.show_solderpaste;
|
||||
|
||||
case B_SilkS:
|
||||
case F_SilkS:
|
||||
return m_Cfg->m_Render.show_silkscreen;
|
||||
|
||||
case B_Mask:
|
||||
case F_Mask:
|
||||
return m_Cfg->m_Render.show_soldermask;
|
||||
|
||||
case Dwgs_User:
|
||||
case Cmts_User:
|
||||
return !m_Cfg->m_Render.realistic && m_Cfg->m_Render.show_comments;
|
||||
|
||||
case Eco1_User:
|
||||
case Eco2_User:
|
||||
return !m_Cfg->m_Render.realistic && m_Cfg->m_Render.show_eco;
|
||||
|
||||
case Edge_Cuts:
|
||||
return !m_Cfg->m_Render.realistic && !m_Cfg->m_Render.show_board_body;
|
||||
|
||||
case Margin:
|
||||
return !m_Cfg->m_Render.realistic;
|
||||
|
||||
case B_Cu:
|
||||
case F_Cu:
|
||||
return !m_board || m_board->IsLayerVisible( aLayer ) || m_Cfg->m_Render.realistic;
|
||||
|
||||
default:
|
||||
// the layer is an internal copper layer, used the visibility
|
||||
return m_board && m_board->IsLayerVisible( aLayer );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BOARD_ADAPTER::IsFootprintShown( FOOTPRINT_ATTR_T aFPAttributes ) const
|
||||
{
|
||||
if( m_IsPreviewer ) // In panel Preview, footprints are always shown, of course
|
||||
return true;
|
||||
|
||||
if( aFPAttributes & FP_EXCLUDE_FROM_POS_FILES )
|
||||
{
|
||||
if( !m_Cfg->m_Render.show_footprints_not_in_posfile )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aFPAttributes & FP_SMD )
|
||||
return m_Cfg->m_Render.show_footprints_insert;
|
||||
|
||||
if( aFPAttributes & FP_THROUGH_HOLE )
|
||||
return m_Cfg->m_Render.show_footprints_normal;
|
||||
|
||||
return m_Cfg->m_Render.show_footprints_virtual;
|
||||
}
|
||||
|
||||
|
||||
int BOARD_ADAPTER::GetHolePlatingThickness() const noexcept
|
||||
{
|
||||
return m_board ? m_board->GetDesignSettings().GetHolePlatingThickness()
|
||||
: DEFAULT_COPPER_THICKNESS;
|
||||
}
|
||||
|
||||
|
||||
unsigned int BOARD_ADAPTER::GetCircleSegmentCount( float aDiameter3DU ) const
|
||||
{
|
||||
wxASSERT( aDiameter3DU > 0.0f );
|
||||
|
||||
return GetCircleSegmentCount( (int)( aDiameter3DU / m_biuTo3Dunits ) );
|
||||
}
|
||||
|
||||
|
||||
unsigned int BOARD_ADAPTER::GetCircleSegmentCount( int aDiameterBIU ) const
|
||||
{
|
||||
wxASSERT( aDiameterBIU > 0 );
|
||||
|
||||
return GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, FULL_CIRCLE );
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::InitSettings" ) );
|
||||
|
||||
if( aStatusReporter )
|
||||
aStatusReporter->Report( _( "Build board outline" ) );
|
||||
|
||||
wxString msg;
|
||||
|
||||
const bool haveOutline = createBoardPolygon( &msg );
|
||||
|
||||
if( aWarningReporter )
|
||||
{
|
||||
if( !haveOutline )
|
||||
aWarningReporter->Report( msg, RPT_SEVERITY_WARNING );
|
||||
else
|
||||
aWarningReporter->Report( wxEmptyString );
|
||||
}
|
||||
|
||||
BOX2I bbbox;
|
||||
|
||||
if( m_board )
|
||||
{
|
||||
bbbox = m_board->ComputeBoundingBox( !m_board->IsFootprintHolder()
|
||||
&& m_Cfg->m_Render.realistic
|
||||
&& haveOutline );
|
||||
}
|
||||
|
||||
// Gives a non null size to avoid issues in zoom / scale calculations
|
||||
if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) )
|
||||
bbbox.Inflate( pcbIUScale.mmToIU( 10 ) );
|
||||
|
||||
m_boardSize = bbbox.GetSize();
|
||||
m_boardPos = bbbox.Centre();
|
||||
|
||||
wxASSERT( (m_boardSize.x > 0) && (m_boardSize.y > 0) );
|
||||
|
||||
m_boardPos.y = -m_boardPos.y; // The y coord is inverted in 3D viewer
|
||||
|
||||
m_copperLayersCount = m_board ? m_board->GetCopperLayerCount() : 2;
|
||||
|
||||
// Ensure the board has 2 sides for 3D views, because it is hard to find
|
||||
// a *really* single side board in the true life...
|
||||
if( m_copperLayersCount < 2 )
|
||||
m_copperLayersCount = 2;
|
||||
|
||||
// Calculate the conversion to apply to all positions.
|
||||
m_biuTo3Dunits = RANGE_SCALE_3D / std::max( m_boardSize.x, m_boardSize.y );
|
||||
|
||||
m_boardBodyThickness3DU = DEFAULT_BOARD_THICKNESS * m_biuTo3Dunits;
|
||||
m_frontCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
|
||||
m_backCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
|
||||
m_nonCopperLayerThickness3DU = DEFAULT_TECH_LAYER_THICKNESS * m_biuTo3Dunits;
|
||||
m_solderPasteLayerThickness3DU = SOLDERPASTE_LAYER_THICKNESS * m_biuTo3Dunits;
|
||||
|
||||
g_BevelThickness3DU = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_3DRT_BevelHeight_um / 1000.0 ) * m_biuTo3Dunits;
|
||||
|
||||
if( m_board )
|
||||
{
|
||||
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
|
||||
if( bds.GetStackupDescriptor().GetCount() )
|
||||
{
|
||||
int thickness = 0;
|
||||
|
||||
for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() )
|
||||
{
|
||||
switch( item->GetType() )
|
||||
{
|
||||
case BS_ITEM_TYPE_DIELECTRIC:
|
||||
for( int sublayer = 0; sublayer < item->GetSublayersCount(); sublayer++ )
|
||||
thickness += item->GetThickness( sublayer );
|
||||
break;
|
||||
|
||||
case BS_ITEM_TYPE_COPPER:
|
||||
if( item->GetBrdLayerId() == F_Cu )
|
||||
m_frontCopperThickness3DU = item->GetThickness() * m_biuTo3Dunits;
|
||||
else if( item->GetBrdLayerId() == B_Cu )
|
||||
m_backCopperThickness3DU = item->GetThickness() * m_biuTo3Dunits;
|
||||
else if( item->IsEnabled() )
|
||||
thickness += item->GetThickness();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_boardBodyThickness3DU = thickness * m_biuTo3Dunits;
|
||||
}
|
||||
}
|
||||
|
||||
// Init Z position of each layer
|
||||
// calculate z position for each copper layer
|
||||
// Zstart = -m_epoxyThickness / 2.0 is the z position of the back (bottom layer) (layer id = 31)
|
||||
// Zstart = +m_epoxyThickness / 2.0 is the z position of the front (top layer) (layer id = 0)
|
||||
|
||||
// ____==__________==________==______ <- Bottom = +m_epoxyThickness / 2.0,
|
||||
// | | Top = Bottom + m_copperThickness
|
||||
// |__________________________________|
|
||||
// == == == == <- Bottom = -m_epoxyThickness / 2.0,
|
||||
// Top = Bottom - m_copperThickness
|
||||
|
||||
unsigned int layer;
|
||||
|
||||
for( layer = 0; layer < m_copperLayersCount; ++layer )
|
||||
{
|
||||
// This approximates internal layer positions (because we're treating all the dielectric
|
||||
// layers as having the same thickness). But we don't render them anyway so it doesn't
|
||||
// really matter.
|
||||
m_layerZcoordBottom[layer] = m_boardBodyThickness3DU / 2.0f -
|
||||
(m_boardBodyThickness3DU * layer / (m_copperLayersCount - 1) );
|
||||
|
||||
if( layer < (m_copperLayersCount / 2) )
|
||||
m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] + m_frontCopperThickness3DU;
|
||||
else
|
||||
m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] - m_backCopperThickness3DU;
|
||||
}
|
||||
|
||||
#define layerThicknessMargin 1.1
|
||||
const float zpos_offset = m_nonCopperLayerThickness3DU * layerThicknessMargin;
|
||||
|
||||
// Fill remaining unused copper layers and back layer zpos with -m_boardBodyThickness / 2.0
|
||||
for( ; layer < MAX_CU_LAYERS; layer++ )
|
||||
{
|
||||
m_layerZcoordBottom[layer] = -( m_boardBodyThickness3DU / 2.0f );
|
||||
m_layerZcoordTop[layer] = -( m_boardBodyThickness3DU / 2.0f ) - m_backCopperThickness3DU;
|
||||
}
|
||||
|
||||
// This is the top of the copper layer thickness.
|
||||
const float zpos_copperTop_back = m_layerZcoordTop[B_Cu];
|
||||
const float zpos_copperTop_front = m_layerZcoordTop[F_Cu];
|
||||
|
||||
// calculate z position for each non copper layer
|
||||
// Solder mask and Solder paste have the same Z position
|
||||
for( int layer_id = MAX_CU_LAYERS; layer_id < PCB_LAYER_ID_COUNT; ++layer_id )
|
||||
{
|
||||
float zposTop;
|
||||
float zposBottom;
|
||||
|
||||
switch( layer_id )
|
||||
{
|
||||
case B_Adhes:
|
||||
zposBottom = zpos_copperTop_back - 2.0f * zpos_offset;
|
||||
zposTop = zposBottom - m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case F_Adhes:
|
||||
zposBottom = zpos_copperTop_front + 2.0f * zpos_offset;
|
||||
zposTop = zposBottom + m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case B_Mask:
|
||||
zposBottom = zpos_copperTop_back;
|
||||
zposTop = zpos_copperTop_back - m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case B_Paste:
|
||||
zposBottom = zpos_copperTop_back;
|
||||
zposTop = zpos_copperTop_back - m_solderPasteLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case F_Mask:
|
||||
zposBottom = zpos_copperTop_front;
|
||||
zposTop = zpos_copperTop_front + m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case F_Paste:
|
||||
zposBottom = zpos_copperTop_front;
|
||||
zposTop = zpos_copperTop_front + m_solderPasteLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case B_SilkS:
|
||||
zposBottom = zpos_copperTop_back - 1.0f * zpos_offset;
|
||||
zposTop = zposBottom - m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
|
||||
case F_SilkS:
|
||||
zposBottom = zpos_copperTop_front + 1.0f * zpos_offset;
|
||||
zposTop = zposBottom + m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
|
||||
// !TODO: review
|
||||
default:
|
||||
zposTop = zpos_copperTop_front + (layer_id - MAX_CU_LAYERS + 3.0f) * zpos_offset;
|
||||
zposBottom = zposTop - m_nonCopperLayerThickness3DU;
|
||||
break;
|
||||
}
|
||||
|
||||
m_layerZcoordTop[layer_id] = zposTop;
|
||||
m_layerZcoordBottom[layer_id] = zposBottom;
|
||||
}
|
||||
|
||||
m_boardCenter = SFVEC3F( m_boardPos.x * m_biuTo3Dunits, m_boardPos.y * m_biuTo3Dunits, 0.0f );
|
||||
|
||||
SFVEC3F boardSize = SFVEC3F( m_boardSize.x * m_biuTo3Dunits, m_boardSize.y * m_biuTo3Dunits,
|
||||
0.0f );
|
||||
boardSize /= 2.0f;
|
||||
|
||||
SFVEC3F boardMin = ( m_boardCenter - boardSize );
|
||||
SFVEC3F boardMax = ( m_boardCenter + boardSize );
|
||||
|
||||
boardMin.z = m_layerZcoordTop[B_Adhes];
|
||||
boardMax.z = m_layerZcoordTop[F_Adhes];
|
||||
|
||||
m_boardBoundingBox = BBOX_3D( boardMin, boardMax );
|
||||
|
||||
#ifdef PRINT_STATISTICS_3D_VIEWER
|
||||
unsigned stats_startCreateBoardPolyTime = GetRunningMicroSecs();
|
||||
#endif
|
||||
|
||||
if( aStatusReporter )
|
||||
aStatusReporter->Report( _( "Create layers" ) );
|
||||
|
||||
createLayers( aStatusReporter );
|
||||
|
||||
COLOR_SETTINGS* colors = Pgm().GetSettingsManager().GetColorSettings();
|
||||
|
||||
auto to_SFVEC4F =
|
||||
[]( const COLOR4D& src )
|
||||
{
|
||||
return SFVEC4F( src.r, src.g, src.b, src.a );
|
||||
};
|
||||
|
||||
m_BgColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_BACKGROUND_TOP ) );
|
||||
m_BgColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_BACKGROUND_BOTTOM ) );
|
||||
|
||||
m_SolderPasteColor = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERPASTE ) );
|
||||
|
||||
if( m_board && colors->GetUseBoardStackupColors() )
|
||||
{
|
||||
const BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
|
||||
|
||||
auto findColor =
|
||||
[]( const wxString& aColorName, const CUSTOM_COLORS_LIST& aColorSet )
|
||||
{
|
||||
if( aColorName.StartsWith( wxT( "#" ) ) )
|
||||
{
|
||||
return KIGFX::COLOR4D( aColorName );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( const CUSTOM_COLOR_ITEM& color : aColorSet )
|
||||
{
|
||||
if( color.m_ColorName == aColorName )
|
||||
return color.m_Color;
|
||||
}
|
||||
}
|
||||
|
||||
return KIGFX::COLOR4D();
|
||||
};
|
||||
|
||||
m_SilkScreenColorTop = to_SFVEC4F( g_DefaultSilkscreen );
|
||||
m_SilkScreenColorBot = to_SFVEC4F( g_DefaultSilkscreen );
|
||||
m_SolderMaskColorTop = to_SFVEC4F( g_DefaultSolderMask );
|
||||
m_SolderMaskColorBot = to_SFVEC4F( g_DefaultSolderMask );
|
||||
|
||||
KIGFX::COLOR4D bodyColor( 0, 0, 0, 0 );
|
||||
|
||||
for( const BOARD_STACKUP_ITEM* stackupItem : stackup.GetList() )
|
||||
{
|
||||
wxString colorName = stackupItem->GetColor();
|
||||
|
||||
switch( stackupItem->GetType() )
|
||||
{
|
||||
case BS_ITEM_TYPE_SILKSCREEN:
|
||||
if( stackupItem->GetBrdLayerId() == F_SilkS )
|
||||
m_SilkScreenColorTop = to_SFVEC4F( findColor( colorName, g_SilkscreenColors ) );
|
||||
else
|
||||
m_SilkScreenColorBot = to_SFVEC4F( findColor( colorName, g_SilkscreenColors ) );
|
||||
break;
|
||||
|
||||
case BS_ITEM_TYPE_SOLDERMASK:
|
||||
if( stackupItem->GetBrdLayerId() == F_Mask )
|
||||
m_SolderMaskColorTop = to_SFVEC4F( findColor( colorName, g_MaskColors ) );
|
||||
else
|
||||
m_SolderMaskColorBot = to_SFVEC4F( findColor( colorName, g_MaskColors ) );
|
||||
|
||||
break;
|
||||
|
||||
case BS_ITEM_TYPE_DIELECTRIC:
|
||||
{
|
||||
KIGFX::COLOR4D layerColor = findColor( colorName, g_BoardColors );
|
||||
|
||||
if( bodyColor == COLOR4D( 0, 0, 0, 0 ) )
|
||||
bodyColor = layerColor;
|
||||
else
|
||||
bodyColor = bodyColor.Mix( layerColor, 1.0 - layerColor.a );
|
||||
|
||||
bodyColor.a += ( 1.0 - bodyColor.a ) * layerColor.a / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bodyColor != COLOR4D( 0, 0, 0, 0 ) )
|
||||
m_BoardBodyColor = to_SFVEC4F( bodyColor );
|
||||
else
|
||||
m_BoardBodyColor = to_SFVEC4F( g_DefaultBoardBody );
|
||||
|
||||
const wxString& finishName = stackup.m_FinishType;
|
||||
|
||||
if( finishName.EndsWith( wxT( "OSP" ) ) )
|
||||
{
|
||||
m_CopperColor = to_SFVEC4F( findColor( wxT( "Copper" ), g_FinishColors ) );
|
||||
}
|
||||
else if( finishName.EndsWith( wxT( "IG" ) )
|
||||
|| finishName.EndsWith( wxT( "gold" ) ) )
|
||||
{
|
||||
m_CopperColor = to_SFVEC4F( findColor( wxT( "Gold" ), g_FinishColors ) );
|
||||
}
|
||||
else if( finishName.StartsWith( wxT( "HAL" ) )
|
||||
|| finishName.StartsWith( wxT( "HASL" ) )
|
||||
|| finishName.EndsWith( wxT( "tin" ) )
|
||||
|| finishName.EndsWith( wxT( "nickel" ) ) )
|
||||
{
|
||||
m_CopperColor = to_SFVEC4F( findColor( wxT( "Tin" ), g_FinishColors ) );
|
||||
}
|
||||
else if( finishName.EndsWith( wxT( "silver" ) ) )
|
||||
{
|
||||
m_CopperColor = to_SFVEC4F( findColor( wxT( "Silver" ), g_FinishColors ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CopperColor = to_SFVEC4F( g_DefaultSurfaceFinish );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SilkScreenColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_SILKSCREEN_BOTTOM ) );
|
||||
m_SilkScreenColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_SILKSCREEN_TOP ) );
|
||||
m_SolderMaskColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERMASK_BOTTOM ) );
|
||||
m_SolderMaskColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERMASK_TOP ) );
|
||||
m_CopperColor = to_SFVEC4F( colors->GetColor( LAYER_3D_COPPER ) );
|
||||
m_BoardBodyColor = to_SFVEC4F( colors->GetColor( LAYER_3D_BOARD ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
int aErrorMax, int aChainingEpsilon,
|
||||
OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr );
|
||||
|
||||
|
||||
bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
|
||||
{
|
||||
m_board_poly.RemoveAllContours();
|
||||
|
||||
if( !m_board )
|
||||
return false;
|
||||
|
||||
bool success;
|
||||
|
||||
if( m_board->IsFootprintHolder() )
|
||||
{
|
||||
if( !m_board->GetFirstFootprint() )
|
||||
{
|
||||
if( aErrorMsg )
|
||||
*aErrorMsg = _( "No footprint loaded." );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
|
||||
|
||||
success = BuildFootprintPolygonOutlines( m_board, m_board_poly,
|
||||
m_board->GetDesignSettings().m_MaxError,
|
||||
chainingEpsilon );
|
||||
|
||||
// Make polygon strictly simple to avoid issues (especially in 3D viewer)
|
||||
m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
||||
if( !success && aErrorMsg )
|
||||
{
|
||||
*aErrorMsg = _( "Footprint outline is missing or malformed. Run Footprint Checker for "
|
||||
"a full analysis." );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = m_board->GetBoardPolygonOutlines( m_board_poly );
|
||||
|
||||
if( !success && aErrorMsg )
|
||||
*aErrorMsg = _( "Board outline is missing or malformed. Run DRC for a full analysis." );
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
float BOARD_ADAPTER::GetFootprintZPos( bool aIsFlipped ) const
|
||||
{
|
||||
if( aIsFlipped )
|
||||
{
|
||||
if( m_Cfg->m_Render.show_solderpaste )
|
||||
return m_layerZcoordBottom[B_SilkS];
|
||||
else
|
||||
return m_layerZcoordBottom[B_Paste];
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_Cfg->m_Render.show_solderpaste )
|
||||
return m_layerZcoordTop[F_SilkS];
|
||||
else
|
||||
return m_layerZcoordTop[F_Paste];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SFVEC4F BOARD_ADAPTER::GetLayerColor( PCB_LAYER_ID aLayerId ) const
|
||||
{
|
||||
wxASSERT( aLayerId < PCB_LAYER_ID_COUNT );
|
||||
|
||||
const COLOR4D color = m_colors->GetColor( aLayerId );
|
||||
|
||||
return SFVEC4F( color.r, color.g, color.b, color.a );
|
||||
}
|
||||
|
||||
|
||||
SFVEC4F BOARD_ADAPTER::GetItemColor( int aItemId ) const
|
||||
{
|
||||
return GetColor( m_colors->GetColor( aItemId ) );
|
||||
}
|
||||
|
||||
|
||||
SFVEC4F BOARD_ADAPTER::GetColor( const COLOR4D& aColor ) const
|
||||
{
|
||||
return SFVEC4F( aColor.r, aColor.g, aColor.b, aColor.a );
|
||||
}
|
||||
|
||||
|
||||
SFVEC2F BOARD_ADAPTER::GetSphericalCoord( int i ) const
|
||||
{
|
||||
SFVEC2F sphericalCoord =
|
||||
SFVEC2F( ( m_Cfg->m_Render.raytrace_lightElevation[i] + 90.0f ) / 180.0f,
|
||||
m_Cfg->m_Render.raytrace_lightAzimuth[i] / 180.0f );
|
||||
|
||||
sphericalCoord.x = glm::clamp( sphericalCoord.x, 0.0f, 1.0f );
|
||||
sphericalCoord.y = glm::clamp( sphericalCoord.y, 0.0f, 2.0f );
|
||||
|
||||
return sphericalCoord;
|
||||
}
|
||||
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef BOARD_ADAPTER_H
|
||||
#define BOARD_ADAPTER_H
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "../3d_rendering/raytracing/accelerators/container_2d.h"
|
||||
#include "../3d_rendering/raytracing/accelerators/container_3d.h"
|
||||
#include "../3d_rendering/raytracing/shapes3D/bbox_3d.h"
|
||||
#include "../3d_rendering/camera.h"
|
||||
#include "../3d_enums.h"
|
||||
#include "../3d_cache/3d_cache.h"
|
||||
#include "../common_ogl/ogl_attr_list.h"
|
||||
#include "../3d_viewer/eda_3d_viewer_settings.h"
|
||||
|
||||
#include <layer_ids.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
#include <pcb_base_frame.h>
|
||||
#include <pcb_text.h>
|
||||
#include <pcb_shape.h>
|
||||
#include <pcb_dimension.h>
|
||||
#include <zone.h>
|
||||
#include <footprint.h>
|
||||
#include <reporter.h>
|
||||
#include <dialogs/dialog_color_picker.h>
|
||||
|
||||
class COLOR_SETTINGS;
|
||||
|
||||
/// A type that stores a container of 2d objects for each layer id
|
||||
typedef std::map<PCB_LAYER_ID, BVH_CONTAINER_2D*> MAP_CONTAINER_2D_BASE;
|
||||
|
||||
/// A type that stores polysets for each layer id
|
||||
typedef std::map<PCB_LAYER_ID, SHAPE_POLY_SET*> MAP_POLY;
|
||||
|
||||
/// This defines the range that all coord will have to be rendered.
|
||||
/// It will use this value to convert to a normalized value between
|
||||
/// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
|
||||
#define RANGE_SCALE_3D 8.0f
|
||||
|
||||
|
||||
/**
|
||||
* Helper class to handle information needed to display 3D board.
|
||||
*/
|
||||
class BOARD_ADAPTER
|
||||
{
|
||||
public:
|
||||
BOARD_ADAPTER();
|
||||
|
||||
~BOARD_ADAPTER();
|
||||
|
||||
/**
|
||||
* Update the cache manager pointer.
|
||||
*
|
||||
* @param aCachePointer: the pointer to the 3D cache manager.
|
||||
*/
|
||||
void Set3dCacheManager( S3D_CACHE* aCacheMgr ) noexcept { m_3dModelManager = aCacheMgr; }
|
||||
S3D_CACHE* Get3dCacheManager() const noexcept { return m_3dModelManager; }
|
||||
|
||||
/**
|
||||
* Check if a layer is enabled.
|
||||
*
|
||||
* @param aLayer layer ID to get status.
|
||||
*/
|
||||
bool Is3dLayerEnabled( PCB_LAYER_ID aLayer ) const;
|
||||
|
||||
/**
|
||||
* Test if footprint should be displayed in relation to attributes and the flags.
|
||||
*/
|
||||
bool IsFootprintShown( FOOTPRINT_ATTR_T aFPAttributes ) const;
|
||||
|
||||
/**
|
||||
* Set current board to be rendered.
|
||||
*
|
||||
* @param aBoard board to process.
|
||||
*/
|
||||
void SetBoard( BOARD* aBoard ) noexcept { m_board = aBoard; }
|
||||
const BOARD* GetBoard() const noexcept { return m_board; }
|
||||
|
||||
void SetColorSettings( COLOR_SETTINGS* aSettings ) noexcept { m_colors = aSettings; }
|
||||
|
||||
/**
|
||||
* Function to be called by the render when it need to reload the settings for the board.
|
||||
*
|
||||
* @param aStatusReporter the pointer for the status reporter.
|
||||
* @param aWarningReporter pointer for the warning reporter.
|
||||
*/
|
||||
void InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningReporter );
|
||||
|
||||
/**
|
||||
* Board integer units To 3D units.
|
||||
*
|
||||
* @return the conversion factor to transform a position from the board to 3D units.
|
||||
*/
|
||||
double BiuTo3dUnits() const noexcept { return m_biuTo3Dunits; }
|
||||
|
||||
/**
|
||||
* Get the board outling bounding box.
|
||||
*
|
||||
* @return the board bounding box in 3D units.
|
||||
*/
|
||||
const BBOX_3D& GetBBox() const noexcept { return m_boardBoundingBox; }
|
||||
|
||||
/**
|
||||
* Get the board body thickness, including internal copper layers (in 3D units).
|
||||
*/
|
||||
float GetBoardBodyThickness() const noexcept { return m_boardBodyThickness3DU; }
|
||||
|
||||
/**
|
||||
* Get the non copper layers thickness (in 3D units).
|
||||
*/
|
||||
float GetNonCopperLayerThickness() const noexcept { return m_nonCopperLayerThickness3DU; }
|
||||
|
||||
/**
|
||||
* Get the copper layer thicknesses (in 3D units).
|
||||
*/
|
||||
float GetFrontCopperThickness() const noexcept { return m_frontCopperThickness3DU; }
|
||||
float GetBackCopperThickness() const noexcept { return m_backCopperThickness3DU; }
|
||||
|
||||
/**
|
||||
* Get the hole plating thickness (NB: in BOARD UNITS!).
|
||||
*/
|
||||
int GetHolePlatingThickness() const noexcept;
|
||||
|
||||
/**
|
||||
* Get the board size.
|
||||
*
|
||||
* @return size in BIU units.
|
||||
*/
|
||||
VECTOR2I GetBoardSize() const noexcept { return m_boardSize; }
|
||||
|
||||
/**
|
||||
* Get the board center.
|
||||
*
|
||||
* @return position in BIU units.
|
||||
*/
|
||||
VECTOR2I GetBoardPos() const noexcept { return m_boardPos; }
|
||||
|
||||
/**
|
||||
* The board center position in 3D units.
|
||||
*
|
||||
* @return board center vector position in 3D units.
|
||||
*/
|
||||
const SFVEC3F& GetBoardCenter() const noexcept { return m_boardCenter; }
|
||||
|
||||
/**
|
||||
* Get the position of the footprint in 3d integer units considering if it is flipped or not.
|
||||
*
|
||||
* @param aIsFlipped true for use in footprints on Front (top) layer, false
|
||||
* if footprint is on back (bottom) layer.
|
||||
* @return the Z position of 3D shapes, in 3D integer units.
|
||||
*/
|
||||
float GetFootprintZPos( bool aIsFlipped ) const ;
|
||||
|
||||
/**
|
||||
* Get the current polygon of the epoxy board.
|
||||
*
|
||||
* @return the shape polygon
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetBoardPoly() const noexcept { return m_board_poly; }
|
||||
|
||||
/**
|
||||
* Get the technical color of a layer.
|
||||
*
|
||||
* @param aLayerId the layer to get the color information.
|
||||
* @return the color in SFVEC3F format.
|
||||
*/
|
||||
SFVEC4F GetLayerColor( PCB_LAYER_ID aLayerId ) const;
|
||||
|
||||
/**
|
||||
* Get the technical color of a layer.
|
||||
*
|
||||
* @param aItemId the item id to get the color information.
|
||||
* @return the color in SFVEC3F format.
|
||||
*/
|
||||
SFVEC4F GetItemColor( int aItemId ) const;
|
||||
|
||||
/**
|
||||
* @param[in] aColor is the color mapped.
|
||||
* @return the color in SFVEC3F format
|
||||
*/
|
||||
SFVEC4F GetColor( const COLOR4D& aColor ) const;
|
||||
|
||||
SFVEC2F GetSphericalCoord( int i ) const;
|
||||
|
||||
/**
|
||||
* Get the top z position.
|
||||
*
|
||||
* @param aLayerId layer id.
|
||||
* @return position in 3D units.
|
||||
*/
|
||||
float GetLayerTopZPos( PCB_LAYER_ID aLayerId ) const noexcept
|
||||
{
|
||||
return m_layerZcoordTop[aLayerId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bottom z position.
|
||||
*
|
||||
* @param aLayerId layer id.
|
||||
* @return position in 3D units.
|
||||
*/
|
||||
float GetLayerBottomZPos( PCB_LAYER_ID aLayerId ) const noexcept
|
||||
{
|
||||
return m_layerZcoordBottom[aLayerId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of containers that have the objects per layer.
|
||||
*
|
||||
* @return the map containers of this board.
|
||||
*/
|
||||
const MAP_CONTAINER_2D_BASE& GetLayerMap() const noexcept { return m_layerMap; }
|
||||
const BVH_CONTAINER_2D* GetPlatedPadsFront() const noexcept { return m_platedPadsFront; }
|
||||
const BVH_CONTAINER_2D* GetPlatedPadsBack() const noexcept { return m_platedPadsBack; }
|
||||
|
||||
const MAP_CONTAINER_2D_BASE& GetLayerHoleMap() const noexcept { return m_layerHoleMap; }
|
||||
const BVH_CONTAINER_2D& GetThroughHoleIds() const noexcept { return m_throughHoleIds; }
|
||||
const BVH_CONTAINER_2D& GetThroughHoleOds() const noexcept { return m_throughHoleOds; }
|
||||
|
||||
/**
|
||||
* Get through hole outside diameter 2D polygons.
|
||||
*
|
||||
* The outside diameter 2D polygon is the hole diameter plus the plating thickness.
|
||||
*
|
||||
* @return a container with through hold outside diameter 2D polygons.
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetThroughHoleOdPolys() const noexcept
|
||||
{
|
||||
return m_throughHoleOdPolys;
|
||||
}
|
||||
|
||||
const BVH_CONTAINER_2D& GetThroughHoleAnnularRings() const noexcept
|
||||
{
|
||||
return m_throughHoleAnnularRings;
|
||||
}
|
||||
|
||||
const SHAPE_POLY_SET& GetThroughHoleAnnularRingPolys() const noexcept
|
||||
{
|
||||
return m_throughHoleAnnularRingPolys;
|
||||
}
|
||||
|
||||
const SHAPE_POLY_SET& GetOuterNonPlatedThroughHolePoly() const noexcept
|
||||
{
|
||||
return m_nonPlatedThroughHoleOdPolys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a container with through hole via hole outside diameters.
|
||||
*/
|
||||
const BVH_CONTAINER_2D& GetThroughHoleViaOds() const noexcept
|
||||
{
|
||||
return m_throughHoleViaOds;
|
||||
}
|
||||
|
||||
const SHAPE_POLY_SET& GetThroughHoleViaOdPolys() const noexcept
|
||||
{
|
||||
return m_throughHoleViaOdPolys;
|
||||
}
|
||||
|
||||
unsigned int GetViaCount() const noexcept { return m_viaCount; }
|
||||
unsigned int GetHoleCount() const noexcept { return m_holeCount; }
|
||||
|
||||
/**
|
||||
* @return via hole average diameter dimension in 3D units.
|
||||
*/
|
||||
float GetAverageViaHoleDiameter() const noexcept { return m_averageViaHoleDiameter; }
|
||||
|
||||
/**
|
||||
* @return the average diameter of through holes in 3D units.
|
||||
*/
|
||||
float GetAverageHoleDiameter() const noexcept { return m_averageHoleDiameter; }
|
||||
|
||||
/**
|
||||
* @return average track width in 3D units.
|
||||
*/
|
||||
float GetAverageTrackWidth() const noexcept { return m_averageTrackWidth; }
|
||||
|
||||
/**
|
||||
* @param aDiameter3DU diameter in 3DU.
|
||||
* @return number of sides that should be used in a circle with \a aDiameter3DU.
|
||||
*/
|
||||
unsigned int GetCircleSegmentCount( float aDiameter3DU ) const;
|
||||
|
||||
/**
|
||||
* @param aDiameterBIU diameter in board internal units.
|
||||
* @return number of sides that should be used in circle with \a aDiameterBIU.
|
||||
*/
|
||||
unsigned int GetCircleSegmentCount( int aDiameterBIU ) const;
|
||||
|
||||
/**
|
||||
* Get map of polygon's layers.
|
||||
*
|
||||
* @return the map with polygon's layers.
|
||||
*/
|
||||
const MAP_POLY& GetPolyMap() const noexcept { return m_layers_poly; }
|
||||
|
||||
const SHAPE_POLY_SET* GetFrontPlatedPadPolys() { return m_frontPlatedPadPolys; }
|
||||
const SHAPE_POLY_SET* GetBackPlatedPadPolys() { return m_backPlatedPadPolys; }
|
||||
const MAP_POLY& GetHoleIdPolysMap() const noexcept { return m_layerHoleIdPolys; }
|
||||
const MAP_POLY& GetHoleOdPolysMap() const noexcept { return m_layerHoleOdPolys; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the board outline polygon.
|
||||
*
|
||||
* @return false if the outline could not be created
|
||||
*/
|
||||
bool createBoardPolygon( wxString* aErrorMsg );
|
||||
void createLayers( REPORTER* aStatusReporter );
|
||||
void destroyLayers();
|
||||
|
||||
// Helper functions to create the board
|
||||
void createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDstContainer );
|
||||
|
||||
void createPadWithMargin( const PAD *aPad, CONTAINER_2D_BASE* aDstContainer,
|
||||
PCB_LAYER_ID aLayer, const VECTOR2I& aMargin ) const;
|
||||
|
||||
OBJECT_2D* createPadWithDrill( const PAD* aPad, int aInflateValue );
|
||||
|
||||
void addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aDstContainer,
|
||||
PCB_LAYER_ID aLayerId, bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads,
|
||||
bool aSkipNonPlatedPads );
|
||||
|
||||
void addFootprintShapes( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aDstContainer,
|
||||
PCB_LAYER_ID aLayerId );
|
||||
|
||||
void addText( const EDA_TEXT* aText, CONTAINER_2D_BASE* aDstContainer,
|
||||
const BOARD_ITEM* aOwner );
|
||||
|
||||
void addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM* aOwner );
|
||||
|
||||
void addShape( const PCB_DIMENSION_BASE* aDimension, CONTAINER_2D_BASE* aDstContainer,
|
||||
const BOARD_ITEM* aOwner );
|
||||
|
||||
void addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aDstContainer,
|
||||
PCB_LAYER_ID aLayerId );
|
||||
|
||||
void transformArcToSegments( const VECTOR2I& aCentre, const VECTOR2I& aStart,
|
||||
const EDA_ANGLE& aArcAngle, int aCircleToSegmentsCount, int aWidth,
|
||||
CONTAINER_2D_BASE* aDstContainer, const BOARD_ITEM& aOwner );
|
||||
|
||||
void buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BASE* aDstContainer, int aWidth );
|
||||
|
||||
// Helper functions to create poly contours
|
||||
void buildPadOutlineAsPolygon( const PAD* aPad, SHAPE_POLY_SET& aBuffer, int aWidth) const;
|
||||
|
||||
void transformFPShapesToPolySet( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aBuffer ) const;
|
||||
|
||||
public:
|
||||
static CUSTOM_COLORS_LIST g_SilkscreenColors;
|
||||
static CUSTOM_COLORS_LIST g_MaskColors;
|
||||
static CUSTOM_COLORS_LIST g_PasteColors;
|
||||
static CUSTOM_COLORS_LIST g_FinishColors;
|
||||
static CUSTOM_COLORS_LIST g_BoardColors;
|
||||
|
||||
static KIGFX::COLOR4D g_DefaultBackgroundTop;
|
||||
static KIGFX::COLOR4D g_DefaultBackgroundBot;
|
||||
static KIGFX::COLOR4D g_DefaultSilkscreen;
|
||||
static KIGFX::COLOR4D g_DefaultSolderMask;
|
||||
static KIGFX::COLOR4D g_DefaultSolderPaste;
|
||||
static KIGFX::COLOR4D g_DefaultSurfaceFinish;
|
||||
static KIGFX::COLOR4D g_DefaultBoardBody;
|
||||
|
||||
public:
|
||||
EDA_3D_VIEWER_SETTINGS* m_Cfg;
|
||||
|
||||
bool m_IsBoardView;
|
||||
bool m_MousewheelPanning;
|
||||
bool m_IsPreviewer; ///< true if the board adaptater is living in a 3D
|
||||
///< preview panel, false for the standard 3D viewer
|
||||
|
||||
SFVEC4F m_BgColorBot; ///< background bottom color
|
||||
SFVEC4F m_BgColorTop; ///< background top color
|
||||
SFVEC4F m_BoardBodyColor; ///< in realistic mode: FR4 board color
|
||||
SFVEC4F m_SolderMaskColorBot; ///< in realistic mode: solder mask color ( bot )
|
||||
SFVEC4F m_SolderMaskColorTop; ///< in realistic mode: solder mask color ( top )
|
||||
SFVEC4F m_SolderPasteColor; ///< in realistic mode: solder paste color
|
||||
SFVEC4F m_SilkScreenColorBot; ///< in realistic mode: SilkScreen color ( bot )
|
||||
SFVEC4F m_SilkScreenColorTop; ///< in realistic mode: SilkScreen color ( top )
|
||||
SFVEC4F m_CopperColor; ///< in realistic mode: copper color
|
||||
|
||||
private:
|
||||
BOARD* m_board;
|
||||
S3D_CACHE* m_3dModelManager;
|
||||
RENDER_SETTINGS* m_renderSettings;
|
||||
COLOR_SETTINGS* m_colors;
|
||||
|
||||
VECTOR2I m_boardPos; ///< Board center position in board internal units.
|
||||
VECTOR2I m_boardSize; ///< Board size in board internal units.
|
||||
SFVEC3F m_boardCenter; ///< 3D center position of the board in 3D units.
|
||||
BBOX_3D m_boardBoundingBox; ///< 3D bounding box of the board in 3D units.
|
||||
|
||||
MAP_POLY m_layers_poly; ///< Amalgamated polygon contours for various types of
|
||||
///< items
|
||||
|
||||
SHAPE_POLY_SET* m_frontPlatedPadPolys;
|
||||
SHAPE_POLY_SET* m_backPlatedPadPolys;
|
||||
|
||||
MAP_POLY m_layerHoleOdPolys; ///< Hole outer diameters (per layer)
|
||||
MAP_POLY m_layerHoleIdPolys; ///< Hole inner diameters (per layer)
|
||||
|
||||
SHAPE_POLY_SET m_nonPlatedThroughHoleOdPolys; ///< NPTH outer diameters
|
||||
SHAPE_POLY_SET m_throughHoleOdPolys; ///< PTH outer diameters
|
||||
SHAPE_POLY_SET m_throughHoleViaOdPolys; ///< Via hole outer diameters
|
||||
SHAPE_POLY_SET m_throughHoleAnnularRingPolys; ///< Via annular ring outer diameters
|
||||
|
||||
SHAPE_POLY_SET m_board_poly; ///< Board outline polygon.
|
||||
|
||||
MAP_CONTAINER_2D_BASE m_layerMap; ///< 2D elements for each layer.
|
||||
MAP_CONTAINER_2D_BASE m_layerHoleMap; ///< Holes for each layer.
|
||||
|
||||
BVH_CONTAINER_2D* m_platedPadsFront;
|
||||
BVH_CONTAINER_2D* m_platedPadsBack;
|
||||
|
||||
BVH_CONTAINER_2D m_throughHoleOds; ///< List of PTH outer diameters
|
||||
BVH_CONTAINER_2D m_throughHoleIds; ///< List of PTH inner diameters
|
||||
BVH_CONTAINER_2D m_throughHoleAnnularRings; ///< List of via annular rings
|
||||
BVH_CONTAINER_2D m_throughHoleViaOds; ///< List of via hole outer diameters
|
||||
BVH_CONTAINER_2D m_throughHoleViaIds; ///< List of via hole inner diameters
|
||||
|
||||
unsigned int m_copperLayersCount;
|
||||
|
||||
double m_biuTo3Dunits; ///< Scale factor to convert board internal units to
|
||||
///< 3D units normalized between -1.0 and 1.0.
|
||||
|
||||
std::array<float, PCB_LAYER_ID_COUNT> m_layerZcoordTop; ///< Top (End) Z position of each
|
||||
///< layer in 3D units.
|
||||
|
||||
std::array<float, PCB_LAYER_ID_COUNT> m_layerZcoordBottom; ///< Bottom (Start) Z position of
|
||||
///< each layer in 3D units.
|
||||
|
||||
float m_frontCopperThickness3DU;
|
||||
float m_backCopperThickness3DU;
|
||||
float m_boardBodyThickness3DU;
|
||||
float m_nonCopperLayerThickness3DU;
|
||||
float m_solderPasteLayerThickness3DU;
|
||||
|
||||
unsigned int m_trackCount;
|
||||
float m_averageTrackWidth;
|
||||
unsigned int m_viaCount;
|
||||
float m_averageViaHoleDiameter;
|
||||
unsigned int m_holeCount;
|
||||
float m_averageHoleDiameter;
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable debug output for this class. Output can be turned
|
||||
* on by setting the WXTRACE environment variable to "KI_TRACE_EDA_CINFO3D_VISU". See the
|
||||
* wxWidgets documentation on wxLogTrace for more information.
|
||||
*/
|
||||
static const wxChar* m_logTrace;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class EDA_3D_BOARD_HOLDER
|
||||
{
|
||||
public:
|
||||
virtual BOARD_ADAPTER& GetAdapter() = 0;
|
||||
virtual CAMERA& GetCurrentCamera() = 0;
|
||||
|
||||
virtual ~EDA_3D_BOARD_HOLDER() {};
|
||||
};
|
||||
|
||||
#endif // BOARD_ADAPTER_H
|
||||
@@ -1,829 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file create_graphic_brd_items.cpp
|
||||
* @brief This file implements the creation of 2D graphic primitives of pcb items:
|
||||
* pads, tracks, drawsegments, texts....
|
||||
* It is based on the function found in the files:
|
||||
* board_items_to_polygon_shape_transform.cpp
|
||||
*/
|
||||
|
||||
#include "../3d_rendering/raytracing/shapes2D/ring_2d.h"
|
||||
#include "../3d_rendering/raytracing/shapes2D/filled_circle_2d.h"
|
||||
#include "../3d_rendering/raytracing/shapes2D/round_segment_2d.h"
|
||||
#include "../3d_rendering/raytracing/shapes2D/triangle_2d.h"
|
||||
#include "fp_textbox.h"
|
||||
#include <board_adapter.h>
|
||||
#include <footprint.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_text.h>
|
||||
#include <fp_shape.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <pcb_painter.h> // for PCB_RENDER_SETTINGS
|
||||
#include <zone.h>
|
||||
#include <fp_text.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <trigo.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <geometry/shape_rect.h>
|
||||
#include <geometry/shape_simple.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wx/log.h>
|
||||
#include <macros.h>
|
||||
#include <callback_gal.h>
|
||||
|
||||
|
||||
#define TO_3DU( x ) ( ( x ) * m_biuTo3Dunits )
|
||||
|
||||
#define TO_SFVEC2F( vec ) SFVEC2F( TO_3DU( vec.x ), TO_3DU( -vec.y ) )
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addText( const EDA_TEXT* aText, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM* aOwner )
|
||||
{
|
||||
KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
|
||||
TEXT_ATTRIBUTES attrs = aText->GetAttributes();
|
||||
float penWidth_3DU = TO_3DU( aText->GetEffectiveTextPenWidth() );
|
||||
KIFONT::FONT* font = aText->GetFont();
|
||||
|
||||
if( !font )
|
||||
font = KIFONT::FONT::GetFont( wxEmptyString, aText->IsBold(), aText->IsItalic() );
|
||||
|
||||
if( aOwner && aOwner->IsKnockout() )
|
||||
{
|
||||
SHAPE_POLY_SET knockouts;
|
||||
|
||||
CALLBACK_GAL callback_gal( empty_opts,
|
||||
// Polygon callback
|
||||
[&]( const SHAPE_LINE_CHAIN& aPoly )
|
||||
{
|
||||
knockouts.AddOutline( aPoly );
|
||||
} );
|
||||
|
||||
attrs.m_StrokeWidth = aText->GetEffectiveTextPenWidth();
|
||||
attrs.m_Angle = aText->GetDrawRotation();
|
||||
|
||||
callback_gal.SetIsFill( font->IsOutline() );
|
||||
callback_gal.SetIsStroke( font->IsStroke() );
|
||||
callback_gal.SetLineWidth( attrs.m_StrokeWidth );
|
||||
font->Draw( &callback_gal, aText->GetShownText(), aText->GetDrawPos(), attrs );
|
||||
|
||||
SHAPE_POLY_SET finalPoly;
|
||||
int margin = attrs.m_StrokeWidth * 1.5 +
|
||||
GetKnockoutTextMargin( attrs.m_Size, attrs.m_StrokeWidth );
|
||||
|
||||
aText->TransformBoundingBoxToPolygon( &finalPoly, margin );
|
||||
finalPoly.BooleanSubtract( knockouts, SHAPE_POLY_SET::PM_FAST );
|
||||
finalPoly.Fracture( SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
ConvertPolygonToTriangles( finalPoly, *aContainer, m_biuTo3Dunits, *aOwner );
|
||||
}
|
||||
else
|
||||
{
|
||||
CALLBACK_GAL callback_gal( empty_opts,
|
||||
// Stroke callback
|
||||
[&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
|
||||
{
|
||||
const SFVEC2F pt1_3DU = TO_SFVEC2F( aPt1 );
|
||||
const SFVEC2F pt2_3DU = TO_SFVEC2F( aPt2 );
|
||||
|
||||
if( penWidth_3DU == 0.0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( Is_segment_a_circle( pt1_3DU, pt2_3DU ) )
|
||||
{
|
||||
// Cannot add segments that have the same start and end point
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( pt1_3DU, penWidth_3DU / 2,
|
||||
*aOwner ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( pt1_3DU, pt2_3DU, penWidth_3DU,
|
||||
*aOwner ) );
|
||||
}
|
||||
},
|
||||
// Triangulation callback
|
||||
[&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
|
||||
{
|
||||
aContainer->Add( new TRIANGLE_2D( TO_SFVEC2F( aPt1 ), TO_SFVEC2F( aPt2 ),
|
||||
TO_SFVEC2F( aPt3 ), *aOwner ) );
|
||||
} );
|
||||
|
||||
attrs.m_Angle = aText->GetDrawRotation();
|
||||
|
||||
font->Draw( &callback_gal, aText->GetShownText(), aText->GetDrawPos(), attrs );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addShape( const PCB_DIMENSION_BASE* aDimension, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM* aOwner )
|
||||
{
|
||||
addText( &aDimension->Text(), aContainer, aDimension );
|
||||
|
||||
const int linewidth = aDimension->GetLineThickness();
|
||||
|
||||
for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
|
||||
{
|
||||
switch( shape->Type() )
|
||||
{
|
||||
case SH_SEGMENT:
|
||||
{
|
||||
const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
|
||||
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( seg.A ), TO_SFVEC2F( seg.B ),
|
||||
TO_3DU( linewidth ), *aOwner ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case SH_CIRCLE:
|
||||
{
|
||||
int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
|
||||
int delta = aDimension->GetLineThickness() / 2;
|
||||
|
||||
aContainer->Add( new RING_2D( TO_SFVEC2F( shape->Centre() ), TO_3DU( radius - delta ),
|
||||
TO_3DU( radius + delta ), *aOwner ) );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aContainer,
|
||||
PCB_LAYER_ID aLayerId )
|
||||
{
|
||||
KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
|
||||
|
||||
if( aFootprint->Reference().GetLayer() == aLayerId && aFootprint->Reference().IsVisible() )
|
||||
addText( &aFootprint->Reference(), aContainer, &aFootprint->Reference() );
|
||||
|
||||
if( aFootprint->Value().GetLayer() == aLayerId && aFootprint->Value().IsVisible() )
|
||||
addText( &aFootprint->Value(), aContainer, &aFootprint->Value() );
|
||||
|
||||
for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
|
||||
{
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_FP_TEXT_T:
|
||||
{
|
||||
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
||||
|
||||
if( text->GetLayer() == aLayerId && text->IsVisible() )
|
||||
addText( text, aContainer, text );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_FP_TEXTBOX_T:
|
||||
{
|
||||
FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
|
||||
|
||||
if( textbox->GetLayer() == aLayerId )
|
||||
{
|
||||
addShape( textbox, aContainer, aFootprint );
|
||||
addText( textbox, aContainer, aFootprint );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_FP_DIM_ALIGNED_T:
|
||||
case PCB_FP_DIM_CENTER_T:
|
||||
case PCB_FP_DIM_ORTHOGONAL_T:
|
||||
case PCB_FP_DIM_RADIAL_T:
|
||||
case PCB_FP_DIM_LEADER_T:
|
||||
{
|
||||
PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( item );
|
||||
|
||||
if( dimension->GetLayer() == aLayerId )
|
||||
addShape( dimension, aContainer, aFootprint );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_FP_SHAPE_T:
|
||||
{
|
||||
FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
|
||||
|
||||
if( shape->GetLayer() == aLayerId )
|
||||
addShape( shape, aContainer, aFootprint );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDstContainer )
|
||||
{
|
||||
SFVEC2F start3DU = TO_SFVEC2F( aTrack->GetStart() );
|
||||
SFVEC2F end3DU = TO_SFVEC2F( aTrack->GetEnd() );
|
||||
|
||||
switch( aTrack->Type() )
|
||||
{
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
const float radius3DU = TO_3DU( aTrack->GetWidth() / 2 );
|
||||
|
||||
if( radius3DU > 0.0 )
|
||||
aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius3DU, *aTrack ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_ARC_T:
|
||||
{
|
||||
const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
|
||||
|
||||
VECTOR2D center( arc->GetCenter() );
|
||||
EDA_ANGLE arc_angle = arc->GetAngle();
|
||||
double radius = arc->GetRadius();
|
||||
int arcsegcount = GetArcToSegmentCount( radius, ARC_HIGH_DEF, arc_angle );
|
||||
int circlesegcount;
|
||||
|
||||
// We need a circle to segment count. However, the arc angle can be small, and the
|
||||
// radius very big. so we calculate a reasonable value for circlesegcount.
|
||||
if( arcsegcount <= 1 ) // The arc will be approximated by a segment
|
||||
{
|
||||
circlesegcount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
circlesegcount = KiROUND( arcsegcount * 360.0 / std::abs( arc_angle.AsDegrees() ) );
|
||||
circlesegcount = std::max( 1, std::min( circlesegcount, 128 ) );
|
||||
}
|
||||
|
||||
transformArcToSegments( VECTOR2I( center.x, center.y ), arc->GetStart(), arc_angle,
|
||||
circlesegcount, arc->GetWidth(), aDstContainer, *arc );
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_TRACE_T: // Track is a usual straight segment
|
||||
{
|
||||
if( aTrack->GetWidth() == 0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( Is_segment_a_circle( start3DU, end3DU ) )
|
||||
{
|
||||
// Cannot add segments that have the same start and end point
|
||||
aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aTrack->GetWidth() / 2 ),
|
||||
*aTrack ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aTrack->GetWidth() ),
|
||||
*aTrack ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aContainer,
|
||||
PCB_LAYER_ID aLayer, const VECTOR2I& aMargin ) const
|
||||
{
|
||||
SHAPE_POLY_SET poly;
|
||||
int maxError = GetBoard()->GetDesignSettings().m_MaxError;
|
||||
VECTOR2I clearance = aMargin;
|
||||
|
||||
// Our shape-based builder can't handle negative or differing x:y clearance values (the
|
||||
// former are common for solder paste while the later get generated when a relative paste
|
||||
// margin is used with an oblong pad). So we apply this huge hack and fake a larger pad to
|
||||
// run the general-purpose polygon builder on.
|
||||
// Of course being a hack it falls down when dealing with custom shape pads (where the size
|
||||
// is only the size of the anchor), so for those we punt and just use aMargin.x.
|
||||
|
||||
if( ( clearance.x < 0 || clearance.x != clearance.y )
|
||||
&& aPad->GetShape() != PAD_SHAPE::CUSTOM )
|
||||
{
|
||||
VECTOR2I dummySize = VECTOR2I( aPad->GetSize() ) + clearance + clearance;
|
||||
|
||||
if( dummySize.x <= 0 || dummySize.y <= 0 )
|
||||
return;
|
||||
|
||||
PAD dummy( *aPad );
|
||||
dummy.SetSize( wxSize( dummySize.x, dummySize.y ) );
|
||||
dummy.TransformShapeToPolygon( poly, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
clearance = { 0, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
auto padShapes = std::static_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
|
||||
|
||||
for( const SHAPE* shape : padShapes->Shapes() )
|
||||
{
|
||||
switch( shape->Type() )
|
||||
{
|
||||
case SH_SEGMENT:
|
||||
{
|
||||
const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*>( shape );
|
||||
|
||||
const SFVEC2F a3DU = TO_SFVEC2F( seg->GetSeg().A );
|
||||
const SFVEC2F b3DU = TO_SFVEC2F( seg->GetSeg().B );
|
||||
const double width3DU = TO_3DU( seg->GetWidth() + clearance.x * 2 );
|
||||
|
||||
if( width3DU == 0.0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( Is_segment_a_circle( a3DU, b3DU ) )
|
||||
{
|
||||
// Cannot add segments that have the same start and end point
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SH_CIRCLE:
|
||||
{
|
||||
const SHAPE_CIRCLE* circle = static_cast<const SHAPE_CIRCLE*>( shape );
|
||||
|
||||
const double radius3DU = TO_3DU( circle->GetRadius() + clearance.x );
|
||||
const SFVEC2F center3DU = TO_SFVEC2F( circle->GetCenter() );
|
||||
|
||||
// Don't render zero radius circles
|
||||
if( radius3DU > 0.0 )
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( center3DU, radius3DU, *aPad ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SH_RECT:
|
||||
{
|
||||
const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>( shape );
|
||||
|
||||
poly.NewOutline();
|
||||
poly.Append( rect->GetPosition() );
|
||||
poly.Append( rect->GetPosition().x + rect->GetSize().x, rect->GetPosition().y );
|
||||
poly.Append( rect->GetPosition() + rect->GetSize() );
|
||||
poly.Append( rect->GetPosition().x, rect->GetPosition().y + rect->GetSize().y );
|
||||
break;
|
||||
}
|
||||
|
||||
case SH_SIMPLE:
|
||||
poly.AddOutline( static_cast<const SHAPE_SIMPLE*>( shape )->Vertices() );
|
||||
break;
|
||||
|
||||
case SH_POLY_SET:
|
||||
poly = *(SHAPE_POLY_SET*) shape;
|
||||
break;
|
||||
|
||||
case SH_ARC:
|
||||
{
|
||||
const SHAPE_ARC* arc = static_cast<const SHAPE_ARC*>( shape );
|
||||
SHAPE_LINE_CHAIN l = arc->ConvertToPolyline( maxError );
|
||||
|
||||
for( int i = 0; i < l.SegmentCount(); i++ )
|
||||
{
|
||||
SHAPE_SEGMENT seg( l.Segment( i ).A, l.Segment( i ).B, arc->GetWidth() );
|
||||
const SFVEC2F a3DU = TO_SFVEC2F( seg.GetSeg().A );
|
||||
const SFVEC2F b3DU = TO_SFVEC2F( seg.GetSeg().B );
|
||||
const double width3DU = TO_3DU( arc->GetWidth() + clearance.x * 2 );
|
||||
|
||||
if( width3DU == 0.0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( Is_segment_a_circle( a3DU, b3DU ) )
|
||||
{
|
||||
// Cannot add segments that have the same start and end point
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNIMPLEMENTED_FOR( SHAPE_TYPE_asString( shape->Type() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !poly.IsEmpty() )
|
||||
{
|
||||
if( clearance.x )
|
||||
poly.Inflate( clearance.x, 32 );
|
||||
|
||||
// Add the PAD polygon
|
||||
ConvertPolygonToTriangles( poly, *aContainer, m_biuTo3Dunits, *aPad );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue )
|
||||
{
|
||||
if( !aPad->HasHole() )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::createPadWithDrill - found an invalid pad" ) );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
|
||||
|
||||
if( slot->GetSeg().A == slot->GetSeg().B )
|
||||
{
|
||||
return new FILLED_CIRCLE_2D( TO_SFVEC2F( slot->GetSeg().A ),
|
||||
TO_3DU( slot->GetWidth() / 2 + aInflateValue ),
|
||||
*aPad );
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ROUND_SEGMENT_2D( TO_SFVEC2F( slot->GetSeg().A ),
|
||||
TO_SFVEC2F( slot->GetSeg().B ),
|
||||
TO_3DU( slot->GetWidth() + aInflateValue * 2 ),
|
||||
*aPad );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aContainer,
|
||||
PCB_LAYER_ID aLayerId, bool aSkipNPTHPadsWihNoCopper,
|
||||
bool aSkipPlatedPads, bool aSkipNonPlatedPads )
|
||||
{
|
||||
for( PAD* pad : aFootprint->Pads() )
|
||||
{
|
||||
if( !pad->IsOnLayer( aLayerId ) )
|
||||
continue;
|
||||
|
||||
// Skip pad annulus when not connected on this layer (if removing is enabled)
|
||||
if( !pad->FlashLayer( aLayerId ) && IsCopperLayer( aLayerId ) )
|
||||
continue;
|
||||
|
||||
// NPTH pads are not drawn on layers if the shape size and pos is the same as their hole:
|
||||
if( aSkipNPTHPadsWihNoCopper && ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
|
||||
{
|
||||
if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
|
||||
{
|
||||
switch( pad->GetShape() )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::OVAL:
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VECTOR2I margin( 0, 0 );
|
||||
|
||||
switch( aLayerId )
|
||||
{
|
||||
case F_Cu:
|
||||
if( aSkipPlatedPads && pad->FlashLayer( F_Mask ) )
|
||||
continue;
|
||||
|
||||
if( aSkipNonPlatedPads && !pad->FlashLayer( F_Mask ) )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
case B_Cu:
|
||||
if( aSkipPlatedPads && pad->FlashLayer( B_Mask ) )
|
||||
continue;
|
||||
|
||||
if( aSkipNonPlatedPads && !pad->FlashLayer( B_Mask ) )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
case F_Mask:
|
||||
case B_Mask:
|
||||
margin.x += pad->GetSolderMaskExpansion();
|
||||
margin.y += pad->GetSolderMaskExpansion();
|
||||
break;
|
||||
|
||||
case F_Paste:
|
||||
case B_Paste:
|
||||
margin += pad->GetSolderPasteMargin();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
createPadWithMargin( pad, aContainer, aLayerId, margin );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// based on TransformArcToPolygon function from
|
||||
// common/convert_basic_shapes_to_polygon.cpp
|
||||
void BOARD_ADAPTER::transformArcToSegments( const VECTOR2I& aCentre, const VECTOR2I& aStart,
|
||||
const EDA_ANGLE& aArcAngle, int aCircleToSegmentsCount,
|
||||
int aWidth, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM& aOwner )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
if( aWidth == 0 )
|
||||
return;
|
||||
|
||||
VECTOR2I arc_start, arc_end;
|
||||
EDA_ANGLE arcAngle( aArcAngle );
|
||||
EDA_ANGLE delta = ANGLE_360 / aCircleToSegmentsCount; // rotate angle
|
||||
|
||||
arc_end = arc_start = aStart;
|
||||
|
||||
if( arcAngle != ANGLE_360 )
|
||||
RotatePoint( arc_end, aCentre, -arcAngle );
|
||||
|
||||
if( arcAngle < ANGLE_0 )
|
||||
{
|
||||
std::swap( arc_start, arc_end );
|
||||
arcAngle = -arcAngle;
|
||||
}
|
||||
|
||||
// Compute the ends of segments and creates poly
|
||||
VECTOR2I curr_end = arc_start;
|
||||
VECTOR2I curr_start = arc_start;
|
||||
|
||||
for( EDA_ANGLE ii = delta; ii < arcAngle; ii += delta )
|
||||
{
|
||||
curr_end = arc_start;
|
||||
RotatePoint( curr_end, aCentre, -ii );
|
||||
|
||||
const SFVEC2F start3DU = TO_SFVEC2F( curr_start );
|
||||
const SFVEC2F end3DU = TO_SFVEC2F( curr_end );
|
||||
|
||||
if( Is_segment_a_circle( start3DU, end3DU ) )
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), aOwner ) );
|
||||
else
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), aOwner ) );
|
||||
|
||||
curr_start = curr_end;
|
||||
}
|
||||
|
||||
if( curr_end != arc_end )
|
||||
{
|
||||
const SFVEC2F start3DU = TO_SFVEC2F( curr_end );
|
||||
const SFVEC2F end3DU = TO_SFVEC2F( arc_end );
|
||||
|
||||
if( Is_segment_a_circle( start3DU, end3DU ) )
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), aOwner ) );
|
||||
else
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), aOwner ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aContainer,
|
||||
const BOARD_ITEM* aOwner )
|
||||
{
|
||||
// The full width of the lines to create
|
||||
// The extra 1 protects the inner/outer radius values from degeneracy
|
||||
const int linewidth = aShape->GetWidth() + 1;
|
||||
PLOT_DASH_TYPE lineStyle = aShape->GetStroke().GetPlotStyle();
|
||||
|
||||
if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE )
|
||||
{
|
||||
switch( aShape->GetShape() )
|
||||
{
|
||||
case SHAPE_T::CIRCLE:
|
||||
{
|
||||
const SFVEC2F center3DU = TO_SFVEC2F( aShape->GetCenter() );
|
||||
float inner_radius3DU = TO_3DU( aShape->GetRadius() - linewidth / 2 );
|
||||
float outer_radius3DU = TO_3DU( aShape->GetRadius() + linewidth / 2 );
|
||||
|
||||
if( inner_radius3DU < 0 )
|
||||
inner_radius3DU = 0.0;
|
||||
|
||||
if( outer_radius3DU == 0.0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( aShape->IsFilled() )
|
||||
{
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( center3DU, outer_radius3DU,
|
||||
*aOwner ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aContainer->Add( new RING_2D( center3DU, inner_radius3DU, outer_radius3DU,
|
||||
*aOwner ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::RECT:
|
||||
if( aShape->IsFilled() )
|
||||
{
|
||||
SHAPE_POLY_SET polyList;
|
||||
|
||||
aShape->TransformShapeToPolygon( polyList, UNDEFINED_LAYER, 0, ARC_HIGH_DEF,
|
||||
ERROR_INSIDE );
|
||||
|
||||
polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
|
||||
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[0] ), TO_SFVEC2F( pts[1] ),
|
||||
TO_3DU( linewidth ), *aOwner ) );
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[1] ), TO_SFVEC2F( pts[2] ),
|
||||
TO_3DU( linewidth ), *aOwner ) );
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[2] ), TO_SFVEC2F( pts[3] ),
|
||||
TO_3DU( linewidth ), *aOwner ) );
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[3] ), TO_SFVEC2F( pts[0] ),
|
||||
TO_3DU( linewidth ), *aOwner ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case SHAPE_T::ARC:
|
||||
{
|
||||
unsigned int segCount = GetCircleSegmentCount( aShape->GetBoundingBox().GetSizeMax() );
|
||||
|
||||
transformArcToSegments( aShape->GetCenter(), aShape->GetStart(), aShape->GetArcAngle(),
|
||||
segCount, linewidth, aContainer, *aOwner );
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::SEGMENT:
|
||||
{
|
||||
const SFVEC2F start3DU = TO_SFVEC2F( aShape->GetStart() );
|
||||
const SFVEC2F end3DU = TO_SFVEC2F( aShape->GetEnd() );
|
||||
const double linewidth3DU = TO_3DU( linewidth );
|
||||
|
||||
if( linewidth3DU == 0.0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( Is_segment_a_circle( start3DU, end3DU ) )
|
||||
{
|
||||
// Cannot add segments that have the same start and end point
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( start3DU, linewidth3DU / 2, *aOwner ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth3DU, *aOwner ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::BEZIER:
|
||||
case SHAPE_T::POLY:
|
||||
{
|
||||
SHAPE_POLY_SET polyList;
|
||||
|
||||
aShape->TransformShapeToPolygon( polyList, UNDEFINED_LAYER, 0, ARC_HIGH_DEF,
|
||||
ERROR_INSIDE );
|
||||
|
||||
if( polyList.IsEmpty() ) // Just for caution
|
||||
break;
|
||||
|
||||
ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxT( "BOARD_ADAPTER::addShape no implementation for " )
|
||||
+ aShape->SHAPE_T_asString() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( linewidth > 0 )
|
||||
{
|
||||
std::vector<SHAPE*> shapes = aShape->MakeEffectiveShapes( true );
|
||||
SFVEC2F a3DU;
|
||||
SFVEC2F b3DU;
|
||||
double width3DU = TO_3DU( linewidth );
|
||||
|
||||
const PCB_PLOT_PARAMS& plotParams = aShape->GetBoard()->GetPlotOptions();
|
||||
KIGFX::PCB_RENDER_SETTINGS renderSettings;
|
||||
|
||||
renderSettings.SetDashLengthRatio( plotParams.GetDashedLineDashRatio() );
|
||||
renderSettings.SetGapLengthRatio( plotParams.GetDashedLineGapRatio() );
|
||||
|
||||
for( SHAPE* shape : shapes )
|
||||
{
|
||||
STROKE_PARAMS::Stroke( shape, lineStyle, linewidth, &renderSettings,
|
||||
[&]( const VECTOR2I& a, const VECTOR2I& b )
|
||||
{
|
||||
a3DU = TO_SFVEC2F( a );
|
||||
b3DU = TO_SFVEC2F( b );
|
||||
|
||||
if( Is_segment_a_circle( a3DU, b3DU ) )
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aOwner ) );
|
||||
else
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aOwner ) );
|
||||
} );
|
||||
}
|
||||
|
||||
for( SHAPE* shape : shapes )
|
||||
delete shape;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aContainer,
|
||||
PCB_LAYER_ID aLayerId )
|
||||
{
|
||||
// This convert the poly in outline and holes
|
||||
ConvertPolygonToTriangles( *aZone->GetFilledPolysList( aLayerId ), *aContainer,
|
||||
m_biuTo3Dunits, *aZone );
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BASE* aContainer,
|
||||
int aWidth )
|
||||
{
|
||||
if( aPad->GetShape() == PAD_SHAPE::CIRCLE ) // Draw a ring
|
||||
{
|
||||
const SFVEC2F center3DU = TO_SFVEC2F( aPad->ShapePos() );
|
||||
const int radius = aPad->GetSize().x / 2;
|
||||
const float inner_radius3DU = TO_3DU( radius - aWidth / 2 );
|
||||
const float outer_radius3DU = TO_3DU( radius + aWidth / 2 );
|
||||
|
||||
aContainer->Add( new RING_2D( center3DU, inner_radius3DU, outer_radius3DU, *aPad ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// For other shapes, add outlines as thick segments in polygon buffer
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
|
||||
const SHAPE_LINE_CHAIN& path = corners->COutline( 0 );
|
||||
|
||||
for( int j = 0; j < path.PointCount(); j++ )
|
||||
{
|
||||
SFVEC2F start3DU = TO_SFVEC2F( path.CPoint( j ) );
|
||||
SFVEC2F end3DU = TO_SFVEC2F( path.CPoint( j + 1 ) );
|
||||
|
||||
if( aWidth == 0 )
|
||||
{
|
||||
// Don't attempt to render degenerate shapes
|
||||
}
|
||||
else if( Is_segment_a_circle( start3DU, end3DU ) )
|
||||
{
|
||||
// Cannot add segments that have the same start and end point
|
||||
aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), *aPad ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), *aPad ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file create_layer_poly.cpp
|
||||
* @brief This file implements the creation of the pcb board items in the poly
|
||||
* contours format.
|
||||
*/
|
||||
|
||||
#include "board_adapter.h"
|
||||
#include <board_design_settings.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <fp_shape.h>
|
||||
#include <footprint.h>
|
||||
|
||||
|
||||
/*
|
||||
* This is used to draw pad outlines on silk layers.
|
||||
*/
|
||||
void BOARD_ADAPTER::buildPadOutlineAsPolygon( const PAD* aPad, SHAPE_POLY_SET& aBuffer,
|
||||
int aWidth ) const
|
||||
{
|
||||
int maxError = m_board->GetDesignSettings().m_MaxError;
|
||||
|
||||
if( aPad->GetShape() == PAD_SHAPE::CIRCLE ) // Draw a ring
|
||||
{
|
||||
TransformRingToPolygon( aBuffer, aPad->ShapePos(), aPad->GetSize().x / 2, aWidth, maxError,
|
||||
ERROR_INSIDE );
|
||||
return;
|
||||
}
|
||||
|
||||
// For other shapes, add outlines as thick segments in polygon buffer
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& corners = aPad->GetEffectivePolygon();
|
||||
const SHAPE_LINE_CHAIN& path = corners->COutline( 0 );
|
||||
|
||||
for( int ii = 0; ii < path.PointCount(); ++ii )
|
||||
{
|
||||
const VECTOR2I& a = path.CPoint( ii );
|
||||
const VECTOR2I& b = path.CPoint( ii + 1 );
|
||||
|
||||
TransformOvalToPolygon( aBuffer, a, b, aWidth, maxError, ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BOARD_ADAPTER::transformFPShapesToPolySet( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aBuffer ) const
|
||||
{
|
||||
int maxError = m_board->GetDesignSettings().m_MaxError;
|
||||
|
||||
for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
|
||||
{
|
||||
if( item->Type() == PCB_FP_SHAPE_T )
|
||||
{
|
||||
FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
|
||||
|
||||
if( shape->GetLayer() == aLayer )
|
||||
shape->TransformShapeToPolygon( aBuffer, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
}
|
||||
else if( BaseType( item->Type() ) == PCB_DIMENSION_T )
|
||||
{
|
||||
PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( item );
|
||||
|
||||
if( dimension->GetLayer() == aLayer )
|
||||
dimension->TransformShapeToPolygon( aBuffer, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef EDA_3D_CANVAS_H
|
||||
#define EDA_3D_CANVAS_H
|
||||
|
||||
#include <atomic>
|
||||
#include "board_adapter.h"
|
||||
#include "3d_rendering/raytracing/accelerators/accelerator_3d.h"
|
||||
#include "3d_rendering/render_3d_base.h"
|
||||
#include "3d_cache/3d_cache.h"
|
||||
#include <gal/hidpi_gl_3D_canvas.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
|
||||
class WX_INFOBAR;
|
||||
class wxStatusBar;
|
||||
class BOARD;
|
||||
class RENDER_3D_RAYTRACE;
|
||||
class RENDER_3D_OPENGL;
|
||||
|
||||
|
||||
/**
|
||||
* Implement a canvas based on a wxGLCanvas
|
||||
*/
|
||||
class EDA_3D_CANVAS : public HIDPI_GL_3D_CANVAS
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a new 3D Canvas with an attribute list.
|
||||
*
|
||||
* @param aParent the parent creator of this canvas.
|
||||
* @param aAttribList a list of openGL options created by GetOpenGL_AttributesList.
|
||||
* @param aBoard The board.
|
||||
* @param aSettings the settings options to be used by this canvas.
|
||||
*/
|
||||
EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList,
|
||||
BOARD_ADAPTER& aSettings, CAMERA& aCamera, S3D_CACHE* a3DCachePointer );
|
||||
|
||||
~EDA_3D_CANVAS();
|
||||
|
||||
/**
|
||||
* Set a dispatcher that processes events and forwards them to tools.
|
||||
*
|
||||
* #DRAW_PANEL_GAL does not take over the ownership. Passing NULL disconnects all event
|
||||
* handlers from the DRAW_PANEL_GAL and parent frame.
|
||||
*
|
||||
* @param aEventDispatcher is the object that will be used for dispatching events.
|
||||
*/
|
||||
void SetEventDispatcher( TOOL_DISPATCHER* aEventDispatcher );
|
||||
|
||||
void SetStatusBar( wxStatusBar* aStatusBar )
|
||||
{
|
||||
m_parentStatusBar = aStatusBar;
|
||||
}
|
||||
|
||||
void SetInfoBar( WX_INFOBAR* aInfoBar )
|
||||
{
|
||||
m_parentInfoBar = aInfoBar;
|
||||
}
|
||||
|
||||
void ReloadRequest( BOARD* aBoard = nullptr, S3D_CACHE* aCachePointer = nullptr );
|
||||
|
||||
/**
|
||||
* Query if there is a pending reload request.
|
||||
*
|
||||
* @return true if it wants to reload, false if there is no reload pending.
|
||||
*/
|
||||
bool IsReloadRequestPending() const
|
||||
{
|
||||
if( m_3d_render )
|
||||
return m_3d_render->IsReloadRequestPending();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current render ( a RENDER_3D_RAYTRACE* or a RENDER_3D_OPENGL* render )
|
||||
*/
|
||||
RENDER_3D_BASE* GetCurrentRender() const { return m_3d_render; }
|
||||
|
||||
/**
|
||||
* Request to render the current view in Raytracing mode.
|
||||
*/
|
||||
void RenderRaytracingRequest();
|
||||
|
||||
/**
|
||||
* Request a screenshot and output it to the \a aDstImage
|
||||
*
|
||||
* @param aDstImage - Screenshot destination image
|
||||
*/
|
||||
void GetScreenshot( wxImage& aDstImage );
|
||||
|
||||
/**
|
||||
* Helper function to call view commands.
|
||||
*
|
||||
* @param aKeycode ascii key commands.
|
||||
* @return true if the key code was handled, false if no command found for this code.
|
||||
*/
|
||||
bool SetView3D( int aKeycode );
|
||||
|
||||
/**
|
||||
* Enable or disable camera animation when switching to a pre-defined view.
|
||||
*/
|
||||
void SetAnimationEnabled( bool aEnable ) { m_animation_enabled = aEnable; }
|
||||
bool GetAnimationEnabled() const { return m_animation_enabled; }
|
||||
|
||||
/**
|
||||
* Set the camera animation moving speed multiplier option.
|
||||
*
|
||||
* @param aMultiplier one of the possible integer options: [1,2,3,4,5].
|
||||
*/
|
||||
void SetMovingSpeedMultiplier( int aMultiplier ) { m_moving_speed_multiplier = aMultiplier; }
|
||||
int GetMovingSpeedMultiplier() const { return m_moving_speed_multiplier; }
|
||||
|
||||
int GetProjectionMode() const { return (int) m_camera.GetProjection(); };
|
||||
void SetProjectionMode( int aMode ) { m_camera.SetProjection( (PROJECTION_TYPE) aMode ); }
|
||||
|
||||
/**
|
||||
* Notify that the render engine was changed.
|
||||
*/
|
||||
void RenderEngineChanged();
|
||||
|
||||
/**
|
||||
* Update the status bar with the position information.
|
||||
*/
|
||||
void DisplayStatus();
|
||||
|
||||
/**
|
||||
* Schedule a refresh update of the canvas.
|
||||
*
|
||||
* @param aRedrawImmediately true will request a redraw, false will schedule a redraw
|
||||
* after a short timeout.
|
||||
*/
|
||||
void Request_refresh( bool aRedrawImmediately = true );
|
||||
|
||||
/**
|
||||
* Used to forward events to the canvas from popups, etc.
|
||||
*/
|
||||
void OnEvent( wxEvent& aEvent );
|
||||
|
||||
/**
|
||||
* Get information used to display 3D board.
|
||||
*/
|
||||
const BOARD_ADAPTER& GetBoardAdapter() const { return m_boardAdapter; }
|
||||
|
||||
/**
|
||||
* Get a value indicating whether to render the pivot.
|
||||
*/
|
||||
bool GetRenderPivot() { return m_render_pivot; }
|
||||
|
||||
/**
|
||||
* Set aValue indicating whether to render the pivot.
|
||||
*
|
||||
* @param aValue true will cause the pivot to be rendered on the next redraw.
|
||||
*/
|
||||
void SetRenderPivot( bool aValue ) { m_render_pivot = aValue; }
|
||||
|
||||
|
||||
#if defined( KICAD_USE_3DCONNEXION )
|
||||
/**
|
||||
* Get a value indicating whether to render the 3dmouse pivot.
|
||||
*/
|
||||
bool GetRender3dmousePivot()
|
||||
{
|
||||
return m_render3dmousePivot;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set aValue indicating whether to render the 3dmouse pivot.
|
||||
*
|
||||
* @param aValue true will cause the pivot to be rendered on the next redraw.
|
||||
*/
|
||||
void SetRender3dmousePivot( bool aValue )
|
||||
{
|
||||
m_render3dmousePivot = aValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the position of the the 3dmouse pivot.
|
||||
*
|
||||
* @param aPos is the position of the 3dmouse rotation pivot
|
||||
*/
|
||||
void Set3dmousePivotPos( const SFVEC3F& aPos )
|
||||
{
|
||||
m_3dmousePivotPos = aPos;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called by a wxPaintEvent event
|
||||
*/
|
||||
void OnPaint( wxPaintEvent& aEvent );
|
||||
|
||||
/**
|
||||
* The actual function to repaint the canvas.
|
||||
*
|
||||
* It is usually called by OnPaint() but because it does not use a wxPaintDC it can be
|
||||
* called outside a wxPaintEvent
|
||||
*/
|
||||
void DoRePaint();
|
||||
|
||||
void OnEraseBackground( wxEraseEvent& event );
|
||||
|
||||
void OnRefreshRequest( wxEvent& aEvent );
|
||||
|
||||
void OnMouseWheel( wxMouseEvent& event );
|
||||
|
||||
#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
|
||||
void OnMagnify( wxMouseEvent& event );
|
||||
#endif
|
||||
|
||||
void OnMouseMove( wxMouseEvent& event );
|
||||
void OnLeftDown( wxMouseEvent& event );
|
||||
void OnLeftUp( wxMouseEvent& event );
|
||||
void OnMiddleUp( wxMouseEvent& event );
|
||||
void OnMiddleDown( wxMouseEvent& event );
|
||||
void OnTimerTimeout_Editing( wxTimerEvent& event );
|
||||
void OnCloseWindow( wxCloseEvent& event );
|
||||
void OnResize( wxSizeEvent& event );
|
||||
void OnTimerTimeout_Redraw( wxTimerEvent& event );
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
/**
|
||||
*Stop the editing time so it will not timeout.
|
||||
*/
|
||||
void stop_editingTimeOut_Timer();
|
||||
|
||||
/**
|
||||
* Reset the editing timer.
|
||||
*/
|
||||
void restart_editingTimeOut_Timer();
|
||||
|
||||
/**
|
||||
* Start a camera movement.
|
||||
*
|
||||
* @param aMovingSpeed the time speed.
|
||||
* @param aRenderPivot if it should display pivot cursor while move.
|
||||
*/
|
||||
void request_start_moving_camera( float aMovingSpeed = 2.0f, bool aRenderPivot = true );
|
||||
|
||||
/**
|
||||
* This function hits a ray to the board and start a movement.
|
||||
*/
|
||||
void move_pivot_based_on_cur_mouse_position();
|
||||
|
||||
/**
|
||||
* Render the pivot cursor.
|
||||
*
|
||||
* @param t time between 0.0 and 1.0.
|
||||
* @param aScale scale to apply on the cursor.
|
||||
*/
|
||||
void render_pivot( float t, float aScale );
|
||||
|
||||
#if defined( KICAD_USE_3DCONNEXION )
|
||||
/**
|
||||
* Render the 3dmouse pivot cursor.
|
||||
*
|
||||
* @param aScale scale to apply on the cursor.
|
||||
*/
|
||||
void render3dmousePivot( float aScale );
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return true if OpenGL initialization succeeded.
|
||||
*/
|
||||
bool initializeOpenGL();
|
||||
|
||||
/**
|
||||
* Free created targets and openGL context.
|
||||
*/
|
||||
void releaseOpenGL();
|
||||
|
||||
RAY getRayAtCurrentMousePosition();
|
||||
|
||||
private:
|
||||
TOOL_DISPATCHER* m_eventDispatcher;
|
||||
wxStatusBar* m_parentStatusBar; // Parent statusbar to report progress
|
||||
WX_INFOBAR* m_parentInfoBar;
|
||||
|
||||
wxGLContext* m_glRC; // Current OpenGL context
|
||||
bool m_is_opengl_initialized;
|
||||
bool m_is_opengl_version_supported;
|
||||
|
||||
wxTimer m_editing_timeout_timer; // Expires after some time signaling that
|
||||
// the mouse / keyboard movements are over
|
||||
wxTimer m_redraw_trigger_timer; // Used to schedule a redraw event
|
||||
std::atomic_flag m_is_currently_painting; // Avoid drawing twice at the same time
|
||||
|
||||
bool m_render_pivot; // Render the pivot while camera moving
|
||||
float m_camera_moving_speed; // 1.0f will be 1:1
|
||||
unsigned m_strtime_camera_movement; // Ticktime of camera movement start
|
||||
bool m_animation_enabled; // Camera animation enabled
|
||||
int m_moving_speed_multiplier; // Camera animation speed multiplier option
|
||||
|
||||
BOARD_ADAPTER& m_boardAdapter; // Pre-computed 3D info and settings
|
||||
RENDER_3D_BASE* m_3d_render;
|
||||
RENDER_3D_RAYTRACE* m_3d_render_raytracing;
|
||||
RENDER_3D_OPENGL* m_3d_render_opengl;
|
||||
|
||||
bool m_opengl_supports_raytracing;
|
||||
bool m_render_raytracing_was_requested;
|
||||
|
||||
ACCELERATOR_3D* m_accelerator3DShapes; // used for mouse over searching
|
||||
|
||||
BOARD_ITEM* m_currentRollOverItem;
|
||||
|
||||
#if defined( KICAD_USE_3DCONNEXION )
|
||||
bool m_render3dmousePivot = false; // Render the 3dmouse pivot
|
||||
SFVEC3F m_3dmousePivotPos; // The position of the 3dmouse pivot
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable the trace output of this class.
|
||||
* The debug output can be turned on by setting the WXTRACE environment variable to
|
||||
* "KI_TRACE_EDA_3D_CANVAS". See the wxWidgets documentation on wxLogTrace for
|
||||
* more information.
|
||||
*/
|
||||
static const wxChar* m_logTrace;
|
||||
};
|
||||
|
||||
|
||||
#endif // EDA_3D_CANVAS_H
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file eda_3d_canvas_pivot.cpp
|
||||
* @brief Implementation of a 3d cursor
|
||||
*/
|
||||
|
||||
#include "../common_ogl/openGL_includes.h"
|
||||
#include "../common_ogl/ogl_utils.h"
|
||||
#include "eda_3d_canvas.h"
|
||||
|
||||
|
||||
static void pivot_render_triangles( float t )
|
||||
{
|
||||
wxASSERT( t >= 0.0f );
|
||||
|
||||
SFVEC3F vertexPointer[12];
|
||||
|
||||
const float u = 1.0f / 6.0f;
|
||||
|
||||
vertexPointer[0] = SFVEC3F( ( -3.0f + t ) * u, -2.0f * u, 0.0f );
|
||||
vertexPointer[1] = SFVEC3F( ( -3.0f + t ) * u, 2.0f * u, 0.0f );
|
||||
vertexPointer[2] = SFVEC3F( ( -1.0f + t ) * u, 0.0f * u, 0.0f );
|
||||
|
||||
vertexPointer[3] = SFVEC3F( -2.0f * u, ( -3.0f + t ) * u, 0.0f );
|
||||
vertexPointer[4] = SFVEC3F( 2.0f * u, ( -3.0f + t ) * u, 0.0f );
|
||||
vertexPointer[5] = SFVEC3F( 0.0f * u, ( -1.0f + t ) * u, 0.0f );
|
||||
|
||||
vertexPointer[6] = SFVEC3F( ( 3.0f - t ) * u, -2.0f * u, 0.0f );
|
||||
vertexPointer[7] = SFVEC3F( ( 3.0f - t ) * u, 2.0f * u, 0.0f );
|
||||
vertexPointer[8] = SFVEC3F( ( 1.0f - t ) * u, 0.0f * u, 0.0f );
|
||||
|
||||
vertexPointer[9] = SFVEC3F( 2.0f * u, ( 3.0f - t ) * u, 0.0f );
|
||||
vertexPointer[10] = SFVEC3F( -2.0f * u, ( 3.0f - t ) * u, 0.0f );
|
||||
vertexPointer[11] = SFVEC3F( 0.0f * u, ( 1.0f - t ) * u, 0.0f );
|
||||
|
||||
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glDisableClientState( GL_COLOR_ARRAY );
|
||||
glDisableClientState( GL_NORMAL_ARRAY );
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glVertexPointer( 3, GL_FLOAT, 0, vertexPointer );
|
||||
|
||||
glEnable( GL_BLEND );
|
||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
|
||||
glDrawArrays( GL_TRIANGLES, 0, 4 * 3 );
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
|
||||
glDisableClientState( GL_VERTEX_ARRAY );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_CANVAS::render_pivot( float t, float aScale )
|
||||
{
|
||||
wxASSERT( aScale >= 0.0f );
|
||||
wxASSERT( t >= 0.0f );
|
||||
|
||||
if( t > 1.0f )
|
||||
t = 1.0f;
|
||||
|
||||
const SFVEC3F &lookAtPos = m_camera.GetLookAtPos_T1();
|
||||
|
||||
glDisable( GL_LIGHTING );
|
||||
glDisable( GL_DEPTH_TEST );
|
||||
glDisable( GL_CULL_FACE );
|
||||
|
||||
// Set projection and modelview matrixes
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
|
||||
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
|
||||
|
||||
glEnable( GL_COLOR_MATERIAL );
|
||||
glColor4f( 0.0f, 1.0f, 0.0f, 0.75f - t * 0.75f );
|
||||
|
||||
// Translate to the look at position
|
||||
glTranslatef( lookAtPos.x, lookAtPos.y, lookAtPos.z );
|
||||
|
||||
glScalef( aScale, aScale, aScale );
|
||||
pivot_render_triangles( t * 0.5f );
|
||||
|
||||
t = t * 0.80f;
|
||||
glScalef( 1.0f - t, 1.0f - t, 1.0f - t );
|
||||
glColor4f( 0.0f, 1.0f, 0.0f, 0.8f - t );
|
||||
|
||||
glPushMatrix();
|
||||
glRotatef( t * 90.0f, 0.0f, 0.0f, 1.0f );
|
||||
pivot_render_triangles( t * 0.5f );
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glRotatef( -t * 90.0f, 0.0f, 0.0f, 1.0f );
|
||||
pivot_render_triangles( t * 0.5f );
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
#if defined( KICAD_USE_3DCONNEXION )
|
||||
void EDA_3D_CANVAS::render3dmousePivot( float aScale )
|
||||
{
|
||||
wxASSERT( aScale >= 0.0f );
|
||||
|
||||
glDisable( GL_LIGHTING );
|
||||
glDisable( GL_DEPTH_TEST );
|
||||
glDisable( GL_CULL_FACE );
|
||||
|
||||
// Set projection and modelview matrixes
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
|
||||
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
|
||||
|
||||
glEnable( GL_COLOR_MATERIAL );
|
||||
glColor4f( 0.0f, 0.667f, 0.902f, 0.75f );
|
||||
|
||||
// Translate to the look at position
|
||||
glTranslatef( m_3dmousePivotPos.x, m_3dmousePivotPos.y, m_3dmousePivotPos.z );
|
||||
|
||||
glPointSize( 16.0f );
|
||||
glEnable( GL_POINT_SMOOTH );
|
||||
glHint( GL_POINT_SMOOTH_HINT, GL_NICEST );
|
||||
|
||||
glEnable( GL_BLEND );
|
||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
|
||||
glScalef( aScale, aScale, aScale );
|
||||
|
||||
// Draw a point at the look at position.
|
||||
glBegin( GL_POINTS );
|
||||
glVertex3f( 0, 0, 0 );
|
||||
glEnd();
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
glDisable( GL_POINT_SMOOTH );
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @file 3d_class.cpp
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <fctsys.h>
|
||||
|
||||
#include <3d_viewer.h>
|
||||
|
||||
S3D_MATERIAL::S3D_MATERIAL( S3D_MASTER* father, const wxString& name ) :
|
||||
EDA_ITEM( father, NOT_USED )
|
||||
{
|
||||
m_DiffuseColor.x = m_DiffuseColor.y = m_DiffuseColor.z = 1.0;
|
||||
m_SpecularColor.x = m_SpecularColor.y = m_SpecularColor.z = 1.0;
|
||||
m_AmbientIntensity = 1.0;
|
||||
m_Transparency = 0.0;
|
||||
m_Shininess = 1.0;
|
||||
m_Name = name;
|
||||
}
|
||||
|
||||
|
||||
void S3D_MATERIAL::SetMaterial()
|
||||
{
|
||||
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
||||
glColor4f( m_DiffuseColor.x * m_AmbientIntensity,
|
||||
m_DiffuseColor.y * m_AmbientIntensity,
|
||||
m_DiffuseColor.z * m_AmbientIntensity,
|
||||
1.0 - m_Transparency );
|
||||
#if 0
|
||||
glColorMaterial( GL_FRONT_AND_BACK, GL_SPECULAR );
|
||||
glColor3f( m_SpecularColor.x, m_SpecularColor.y, m_SpecularColor.z );
|
||||
#endif
|
||||
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
||||
}
|
||||
|
||||
|
||||
void S3D_MASTER::Copy( S3D_MASTER* pattern )
|
||||
{
|
||||
SetShape3DName( pattern->GetShape3DName() );
|
||||
m_MatScale = pattern->m_MatScale;
|
||||
m_MatRotation = pattern->m_MatRotation;
|
||||
m_MatPosition = pattern->m_MatPosition;
|
||||
m_3D_Drawings = NULL;
|
||||
m_Materials = NULL;
|
||||
}
|
||||
|
||||
|
||||
S3D_MASTER::S3D_MASTER( EDA_ITEM* aParent ) :
|
||||
EDA_ITEM( aParent, NOT_USED )
|
||||
{
|
||||
m_MatScale.x = m_MatScale.y = m_MatScale.z = 1.0;
|
||||
m_3D_Drawings = NULL;
|
||||
m_Materials = NULL;
|
||||
m_ShapeType = FILE3D_NONE;
|
||||
}
|
||||
|
||||
|
||||
S3D_MASTER:: ~S3D_MASTER()
|
||||
{
|
||||
STRUCT_3D_SHAPE* next;
|
||||
S3D_MATERIAL* nextmat;
|
||||
|
||||
for( ; m_3D_Drawings != NULL; m_3D_Drawings = next )
|
||||
{
|
||||
next = m_3D_Drawings->Next();
|
||||
delete m_3D_Drawings;
|
||||
}
|
||||
|
||||
for( ; m_Materials != NULL; m_Materials = nextmat )
|
||||
{
|
||||
nextmat = m_Materials->Next();
|
||||
delete m_Materials;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool S3D_MASTER::Is3DType( enum FILE3D_TYPE aShapeType )
|
||||
{
|
||||
// type 'none' is not valid and will always return false
|
||||
if( aShapeType == FILE3D_NONE )
|
||||
return false;
|
||||
|
||||
// no one is interested if we have no file
|
||||
if( m_Shape3DName.empty() )
|
||||
return false;
|
||||
|
||||
if( aShapeType == m_ShapeType )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void S3D_MASTER::SetShape3DName( const wxString& aShapeName )
|
||||
{
|
||||
m_ShapeType = FILE3D_NONE;
|
||||
m_Shape3DName = aShapeName;
|
||||
|
||||
if( m_Shape3DName.empty() )
|
||||
return;
|
||||
|
||||
wxFileName fn = m_Shape3DName;
|
||||
wxString ext = fn.GetExt();
|
||||
|
||||
if( ext == wxT( "wrl" ) || ext == wxT( "x3d" ) )
|
||||
m_ShapeType = FILE3D_VRML;
|
||||
else if( ext == wxT( "idf" ) )
|
||||
m_ShapeType = FILE3D_IDF;
|
||||
else
|
||||
m_ShapeType = FILE3D_UNKNOWN;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
STRUCT_3D_SHAPE::STRUCT_3D_SHAPE( EDA_ITEM* aParent ) :
|
||||
EDA_ITEM( aParent, NOT_USED )
|
||||
{
|
||||
m_3D_Coord = NULL;
|
||||
m_3D_CoordIndex = NULL;
|
||||
m_3D_Points = 0;
|
||||
}
|
||||
|
||||
|
||||
STRUCT_3D_SHAPE:: ~STRUCT_3D_SHAPE()
|
||||
{
|
||||
delete m_3D_Coord;
|
||||
delete m_3D_CoordIndex;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_draw_basic_functions.cpp
|
||||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <trigo.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
|
||||
#include <3d_viewer.h>
|
||||
#include <info3d_visu.h>
|
||||
#include <3d_draw_basic_functions.h>
|
||||
|
||||
// Imported function:
|
||||
extern void Set_Object_Data( std::vector<S3D_VERTEX>& aVertices, double aBiuTo3DUnits );
|
||||
extern void CheckGLError();
|
||||
|
||||
// Number of segments to approximate a circle by segments
|
||||
#define SEGM_PER_CIRCLE 16
|
||||
|
||||
#ifndef CALLBACK
|
||||
#define CALLBACK
|
||||
#endif
|
||||
|
||||
// CALLBACK functions for GLU_TESS
|
||||
static void CALLBACK tessBeginCB( GLenum which );
|
||||
static void CALLBACK tessEndCB();
|
||||
static void CALLBACK tessErrorCB( GLenum errorCode );
|
||||
static void CALLBACK tessCPolyPt2Vertex( const GLvoid* data );
|
||||
|
||||
// 2 helper functions to set the current normal vector for gle items
|
||||
static inline void SetNormalZpos()
|
||||
{
|
||||
glNormal3f( 0.0, 0.0, 1.0 );
|
||||
}
|
||||
|
||||
static inline void SetNormalZneg()
|
||||
{
|
||||
glNormal3f( 0.0, 0.0, -1.0 );
|
||||
}
|
||||
|
||||
|
||||
/* Draw3D_VerticalPolygonalCylinder is a helper function.
|
||||
*
|
||||
* draws a "vertical cylinder" having a polygon shape
|
||||
* from Z position = aZpos to aZpos + aHeight
|
||||
* Used to create the vertical sides of 3D horizontal shapes with thickness.
|
||||
*/
|
||||
static void Draw3D_VerticalPolygonalCylinder( const CPOLYGONS_LIST& aPolysList,
|
||||
int aHeight, int aZpos,
|
||||
bool aInside, double aBiuTo3DUnits )
|
||||
{
|
||||
if( aHeight == 0 )
|
||||
return;
|
||||
|
||||
std::vector<S3D_VERTEX> coords;
|
||||
coords.resize( 4 );
|
||||
|
||||
// Init Z position of the 4 points of a GL_QUAD
|
||||
if( aInside )
|
||||
{
|
||||
coords[0].z = aZpos;
|
||||
coords[1].z = aZpos + aHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
coords[0].z = aZpos + aHeight;
|
||||
coords[1].z = aZpos;
|
||||
}
|
||||
coords[2].z = coords[1].z;
|
||||
coords[3].z = coords[0].z;
|
||||
|
||||
// Draw the vertical polygonal side
|
||||
int startContour = 0;
|
||||
for( unsigned ii = 0; ii < aPolysList.GetCornersCount(); ii++ )
|
||||
{
|
||||
unsigned jj = ii + 1;
|
||||
|
||||
if( aPolysList.IsEndContour( ii ) || jj >= aPolysList.GetCornersCount() )
|
||||
{
|
||||
jj = startContour;
|
||||
startContour = ii + 1;
|
||||
}
|
||||
|
||||
// Build the 4 vertices of each GL_QUAD
|
||||
coords[0].x = aPolysList.GetX( ii );
|
||||
coords[0].y = -aPolysList.GetY( ii );
|
||||
coords[1].x = coords[0].x;
|
||||
coords[1].y = coords[0].y; // only z change
|
||||
coords[2].x = aPolysList.GetX( jj );
|
||||
coords[2].y = -aPolysList.GetY( jj );
|
||||
coords[3].x = coords[2].x;
|
||||
coords[3].y = coords[2].y; // only z change
|
||||
|
||||
// Creates the GL_QUAD
|
||||
Set_Object_Data( coords, aBiuTo3DUnits );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SetGLColor( EDA_COLOR_T color, double alpha )
|
||||
{
|
||||
double red, green, blue;
|
||||
const StructColors &colordata = g_ColorRefs[ColorGetBase( color )];
|
||||
|
||||
red = colordata.m_Red / 255.0;
|
||||
blue = colordata.m_Blue / 255.0;
|
||||
green = colordata.m_Green / 255.0;
|
||||
glColor4f( red, green, blue, alpha );
|
||||
}
|
||||
|
||||
|
||||
/* draw all solid polygons found in aPolysList
|
||||
* aZpos = z position in board internal units
|
||||
* aThickness = thickness in board internal units
|
||||
* If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos.
|
||||
* If aThickness > 0, a solid object is drawn.
|
||||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolyPolygons( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness, double aBiuTo3DUnits )
|
||||
{
|
||||
GLUtesselator* tess = gluNewTess();
|
||||
|
||||
gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )tessBeginCB );
|
||||
gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )tessEndCB );
|
||||
gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )tessErrorCB );
|
||||
gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )tessCPolyPt2Vertex );
|
||||
|
||||
GLdouble v_data[3];
|
||||
double zpos = ( aZpos + (aThickness / 2) ) * aBiuTo3DUnits;
|
||||
g_Parm_3D_Visu.m_CurrentZpos = zpos;
|
||||
v_data[2] = aZpos + (aThickness / 2);
|
||||
|
||||
// Set normal to toward positive Z axis, for a solid object only (to draw the top side)
|
||||
if( aThickness )
|
||||
SetNormalZpos();
|
||||
|
||||
// gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
|
||||
|
||||
// Draw solid areas contained in this list
|
||||
CPOLYGONS_LIST polylist = aPolysList; // temporary copy for gluTessVertex
|
||||
|
||||
for( int side = 0; side < 2; side++ )
|
||||
{
|
||||
int startContour = 1;
|
||||
|
||||
for( unsigned ii = 0; ii < polylist.GetCornersCount(); ii++ )
|
||||
{
|
||||
if( startContour == 1 )
|
||||
{
|
||||
gluTessBeginPolygon( tess, NULL );
|
||||
gluTessBeginContour( tess );
|
||||
startContour = 0;
|
||||
}
|
||||
|
||||
v_data[0] = polylist.GetX( ii ) * aBiuTo3DUnits;
|
||||
v_data[1] = -polylist.GetY( ii ) * aBiuTo3DUnits;
|
||||
// gluTessVertex store pointers on data, not data, so do not store
|
||||
// different corners values in a temporary variable
|
||||
// but send pointer on each CPolyPt value in polylist
|
||||
// before calling gluDeleteTess
|
||||
gluTessVertex( tess, v_data, &polylist[ii] );
|
||||
|
||||
if( polylist.IsEndContour( ii ) )
|
||||
{
|
||||
gluTessEndContour( tess );
|
||||
gluTessEndPolygon( tess );
|
||||
startContour = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( aThickness == 0 )
|
||||
break;
|
||||
|
||||
// Prepare the bottom side of solid areas
|
||||
zpos = ( aZpos - (aThickness / 2) ) * aBiuTo3DUnits;
|
||||
g_Parm_3D_Visu.m_CurrentZpos = zpos;
|
||||
v_data[2] = zpos;
|
||||
// Now;, set normal to toward negative Z axis, for the solid object bottom side
|
||||
SetNormalZneg();
|
||||
}
|
||||
|
||||
gluDeleteTess( tess );
|
||||
|
||||
if( aThickness == 0 )
|
||||
return;
|
||||
|
||||
// Build the 3D data : vertical side
|
||||
Draw3D_VerticalPolygonalCylinder( polylist, aThickness, aZpos - (aThickness / 2), false, aBiuTo3DUnits );
|
||||
}
|
||||
|
||||
|
||||
/* draw the solid polygon found in aPolysList
|
||||
* The first polygon is the main polygon, others are holes
|
||||
* See Draw3D_SolidHorizontalPolyPolygons for more info
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolygonWithHoles( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness,
|
||||
double aBiuTo3DUnits )
|
||||
{
|
||||
CPOLYGONS_LIST polygon;
|
||||
|
||||
ConvertPolysListWithHolesToOnePolygon( aPolysList, polygon );
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, aThickness, aBiuTo3DUnits );
|
||||
}
|
||||
|
||||
|
||||
/* draw a cylinder (a tube) using 3D primitives.
|
||||
* the cylinder axis is parallel to the Z axis
|
||||
* If aHeight = height of the cylinder is 0, only one ring will be drawn
|
||||
* If aThickness = 0, only one cylinder will be drawn
|
||||
*/
|
||||
void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
|
||||
int aHeight, int aThickness,
|
||||
int aZpos, double aBiuTo3DUnits )
|
||||
{
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
CPOLYGONS_LIST outer_cornerBuffer;
|
||||
|
||||
TransformCircleToPolygon( outer_cornerBuffer, aCenterPos,
|
||||
aRadius + (aThickness / 2), slice );
|
||||
|
||||
std::vector<S3D_VERTEX> coords;
|
||||
coords.resize( 4 );
|
||||
|
||||
CPOLYGONS_LIST inner_cornerBuffer;
|
||||
if( aThickness ) // build the the vertical inner polygon (hole)
|
||||
TransformCircleToPolygon( inner_cornerBuffer, aCenterPos,
|
||||
aRadius - (aThickness / 2), slice );
|
||||
|
||||
if( aHeight )
|
||||
{
|
||||
// Draw the vertical outer side
|
||||
Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer,
|
||||
aHeight, aZpos, false, aBiuTo3DUnits );
|
||||
if( aThickness )
|
||||
// Draws the vertical inner side (hole)
|
||||
Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer,
|
||||
aHeight, aZpos, true, aBiuTo3DUnits );
|
||||
}
|
||||
|
||||
if( aThickness )
|
||||
{
|
||||
// draw top (front) and bottom (back) horizontal sides (rings)
|
||||
SetNormalZpos();
|
||||
outer_cornerBuffer.Append( inner_cornerBuffer );
|
||||
CPOLYGONS_LIST polygon;
|
||||
|
||||
ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
|
||||
// draw top (front) horizontal ring
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits );
|
||||
|
||||
if( aHeight )
|
||||
{
|
||||
// draw bottom (back) horizontal ring
|
||||
SetNormalZneg();
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, 0, aBiuTo3DUnits );
|
||||
}
|
||||
}
|
||||
|
||||
SetNormalZpos();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Function Draw3D_ZaxisOblongCylinder:
|
||||
* draw a segment with an oblong hole.
|
||||
* Used to draw oblong holes
|
||||
* If aHeight = height of the cylinder is 0, only one ring will be drawn
|
||||
* If aThickness = 0, only one cylinder will be drawn
|
||||
*/
|
||||
void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
|
||||
int aRadius, int aHeight, int aThickness,
|
||||
int aZpos, double aBiuTo3DUnits )
|
||||
{
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
|
||||
// Build the points to approximate oblong cylinder by segments
|
||||
CPOLYGONS_LIST outer_cornerBuffer;
|
||||
|
||||
int segm_width = (aRadius * 2) + aThickness;
|
||||
TransformRoundedEndsSegmentToPolygon( outer_cornerBuffer, aAxis1Pos,
|
||||
aAxis2Pos, slice, segm_width );
|
||||
|
||||
// Draw the oblong outer cylinder
|
||||
if( aHeight )
|
||||
Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer, aHeight, aZpos,
|
||||
false, aBiuTo3DUnits );
|
||||
|
||||
if( aThickness )
|
||||
{
|
||||
CPOLYGONS_LIST inner_cornerBuffer;
|
||||
segm_width = aRadius * 2;
|
||||
TransformRoundedEndsSegmentToPolygon( inner_cornerBuffer, aAxis1Pos,
|
||||
aAxis2Pos, slice, segm_width );
|
||||
|
||||
// Draw the oblong inner cylinder
|
||||
if( aHeight )
|
||||
Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer, aHeight,
|
||||
aZpos, true, aBiuTo3DUnits );
|
||||
|
||||
// Build the horizontal full polygon shape
|
||||
// (outer polygon shape - inner polygon shape)
|
||||
outer_cornerBuffer.Append( inner_cornerBuffer );
|
||||
|
||||
CPOLYGONS_LIST polygon;
|
||||
ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
|
||||
|
||||
// draw top (front) horizontal side (ring)
|
||||
SetNormalZpos();
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits );
|
||||
|
||||
if( aHeight )
|
||||
{
|
||||
// draw bottom (back) horizontal side (ring)
|
||||
SetNormalZneg();
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, 0, aBiuTo3DUnits );
|
||||
}
|
||||
}
|
||||
|
||||
SetNormalZpos();
|
||||
}
|
||||
|
||||
|
||||
/* draw a thick segment using 3D primitives, in a XY plane
|
||||
* wxPoint aStart, wxPoint aEnd = YX position of end in board units
|
||||
* aWidth = width of segment in board units
|
||||
* aThickness = thickness of segment in board units
|
||||
* aZpos = z position of segment in board units
|
||||
*/
|
||||
void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
|
||||
int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits )
|
||||
{
|
||||
CPOLYGONS_LIST cornerBuffer;
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( cornerBuffer, aStart, aEnd, slice, aWidth );
|
||||
|
||||
Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits );
|
||||
}
|
||||
|
||||
|
||||
void Draw3D_ArcSegment( const wxPoint& aCenterPos, const wxPoint& aStartPoint,
|
||||
double aArcAngle, int aWidth, int aThickness,
|
||||
int aZpos, double aBiuTo3DUnits )
|
||||
{
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
|
||||
CPOLYGONS_LIST cornerBuffer;
|
||||
TransformArcToPolygon( cornerBuffer, aCenterPos, aStartPoint, aArcAngle,
|
||||
slice, aWidth );
|
||||
|
||||
Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits );
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
// GLU_TESS CALLBACKS
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CALLBACK tessBeginCB( GLenum which )
|
||||
{
|
||||
glBegin( which );
|
||||
}
|
||||
|
||||
|
||||
void CALLBACK tessEndCB()
|
||||
{
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
void CALLBACK tessCPolyPt2Vertex( const GLvoid* data )
|
||||
{
|
||||
// cast back to double type
|
||||
const CPolyPt* ptr = (const CPolyPt*) data;
|
||||
|
||||
glVertex3d( ptr->x * g_Parm_3D_Visu.m_BiuTo3Dunits,
|
||||
-ptr->y * g_Parm_3D_Visu.m_BiuTo3Dunits,
|
||||
g_Parm_3D_Visu.m_CurrentZpos );
|
||||
}
|
||||
|
||||
|
||||
void CALLBACK tessErrorCB( GLenum errorCode )
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
const GLubyte* errorStr;
|
||||
|
||||
errorStr = gluErrorString( errorCode );
|
||||
|
||||
// DEBUG //
|
||||
DBG( printf( "Tess ERROR: %s\n", errorStr ); )
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_draw_basic_functions.h
|
||||
*/
|
||||
#ifndef _3D_DRAW_BASIC_FUNCTIONS_H_
|
||||
#define _3D_DRAW_BASIC_FUNCTIONS_H_
|
||||
|
||||
// angle increment to draw a circle, approximated by segments
|
||||
#define ANGLE_INC( x ) ( 3600 / (x) )
|
||||
|
||||
/** draw all solid polygons found in aPolysList
|
||||
* @param aPolysList = the poligon list to draw
|
||||
* @param aZpos = z position in board internal units
|
||||
* @param aThickness = thickness in board internal units
|
||||
* @param aBiuTo3DUnits = board internal units to 3D units scaling value
|
||||
* If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos.
|
||||
* If aThickness > 0, a solid object is drawn.
|
||||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolyPolygons( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness, double aBiuTo3DUnits );
|
||||
|
||||
/** draw the solid polygon found in aPolysList
|
||||
* The first polygonj is the main polygon, others are holes
|
||||
* @param aPolysList = the polygon with holes to draw
|
||||
* @param aZpos = z position in board internal units
|
||||
* @param aThickness = thickness in board internal units
|
||||
* @param aBiuTo3DUnits = board internal units to 3D units scaling value
|
||||
* If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos.
|
||||
* If aThickness > 0, a solid object is drawn.
|
||||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolygonWithHoles( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness, double aBiuTo3DUnits );
|
||||
|
||||
/** draw a thick segment using 3D primitives, in a XY plane
|
||||
* @param aStart = YX position of start point in board units
|
||||
* @param aEnd = YX position of end point in board units
|
||||
* @param aWidth = width of segment in board units
|
||||
* @param aThickness = thickness of segment in board units
|
||||
* @param aZpos = z position of segment in board units
|
||||
* @param aBiuTo3DUnits = board internal units to 3D units scaling value
|
||||
* If aThickness = 0, a polygon area is drawn in a XY plane at Z position = aZpos.
|
||||
* If aThickness > 0, a solid object is drawn.
|
||||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
|
||||
int aWidth, int aThickness, int aZpos,
|
||||
double aBiuTo3DUnits );
|
||||
|
||||
/** draw an arc using 3D primitives, in a XY plane
|
||||
* @param aCenterPos = XY position of the center in board units
|
||||
* @param aStartPoint = start point coordinate of arc in board units
|
||||
* @param aWidth = width of the circle in board units
|
||||
* @param aArcAngle = arc angle in 1/10 degrees
|
||||
* @param aWidth = thickness of arc
|
||||
* @param aThickness = thickness of segment in board units
|
||||
* @param aZpos = z position of segment in board units
|
||||
* @param aBiuTo3DUnits = board internal units to 3D units scaling value
|
||||
*/
|
||||
void Draw3D_ArcSegment( const wxPoint& aCenterPos, const wxPoint& aStartPoint,
|
||||
double aArcAngle, int aWidth, int aThickness,
|
||||
int aZpos, double aBiuTo3DUnits );
|
||||
|
||||
|
||||
/** draw a thick cylinder (a tube) using 3D primitives.
|
||||
* the cylinder axis is parallel to the Z axis
|
||||
* @param aCenterPos = XY position of the axis cylinder ( board internal units)
|
||||
* @param aRadius = radius of the cylinder ( board internal units)
|
||||
* @param aHeight = height of the cylinder ( boardinternal units)
|
||||
* @param aThickness = tichkness of tube ( boardinternal units)
|
||||
* @param aZpos = Z position of the bottom side of the cylinder ( board internal units)
|
||||
* @param aBiuTo3DUnits = board internal units to 3D units scaling value
|
||||
*
|
||||
* If aHeight = height of the cylinder is 0, only one ring will be drawn
|
||||
* If aThickness = 0, only one cylinder (not a tube) will be drawn
|
||||
*/
|
||||
void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
|
||||
int aHeight, int aThickness,
|
||||
int aZpos, double aBiuTo3DUnits );
|
||||
|
||||
/** draw an oblong cylinder (oblong tube) using 3D primitives.
|
||||
* the cylinder axis are parallel to the Z axis
|
||||
* @param aAxis1Pos = position of the first axis cylinder
|
||||
* @param aAxis2Pos = position of the second axis cylinder
|
||||
* @param aRadius = radius of the cylinder ( board internal units )
|
||||
* @param aHeight = height of the cylinder ( board internal units )
|
||||
* @param aThickness = tichkness of tube ( board internal units )
|
||||
* @param aZpos = Z position of the bottom side of the cylinder ( board internal units )
|
||||
* @param aBiuTo3DUnits = board internal units to 3D units scaling value
|
||||
*/
|
||||
void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
|
||||
int aRadius, int aHeight, int aThickness,
|
||||
int aZpos, double aBiuTo3DUnits );
|
||||
/**
|
||||
* Set the current 3D color from a Kicad color, with optional transparency
|
||||
* @param aColor = a EDA_COLOR_T kicad color index
|
||||
* @param aTransparency = the color transparency (default = 1.0 = no transparency)
|
||||
*/
|
||||
void SetGLColor( EDA_COLOR_T aColor, double aTransparency = 1.0 );
|
||||
|
||||
|
||||
#endif // _3D_DRAW_BASIC_FUNCTIONS_H_
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_enums.h
|
||||
* @brief declared enumerations and flags
|
||||
*/
|
||||
|
||||
#ifndef _3D_ENUMS_H_
|
||||
#define _3D_ENUMS_H_
|
||||
|
||||
/// Rotation direction for the 3d canvas
|
||||
enum class ROTATION_DIR
|
||||
{
|
||||
X_CW,
|
||||
X_CCW,
|
||||
Y_CW,
|
||||
Y_CCW,
|
||||
Z_CW,
|
||||
Z_CCW
|
||||
};
|
||||
|
||||
/// Camera types
|
||||
enum class CAMERA_TYPE
|
||||
{
|
||||
TRACKBALL
|
||||
};
|
||||
|
||||
// Do not modify the following enum numbers, they are saved in the config file and used in the UI
|
||||
|
||||
/// Grid types
|
||||
enum class GRID3D_TYPE
|
||||
{
|
||||
NONE = 0,
|
||||
GRID_1MM = 1,
|
||||
GRID_2P5MM = 2,
|
||||
GRID_5MM = 3,
|
||||
GRID_10MM = 4
|
||||
};
|
||||
|
||||
/// Render engine mode
|
||||
enum class RENDER_ENGINE
|
||||
{
|
||||
OPENGL = 0,
|
||||
RAYTRACING = 1,
|
||||
};
|
||||
|
||||
/// Render 3d model shape materials mode
|
||||
enum class MATERIAL_MODE
|
||||
{
|
||||
NORMAL = 0, ///< Use all material properties from model file
|
||||
DIFFUSE_ONLY = 1, ///< Use only diffuse material properties
|
||||
CAD_MODE = 2 ///< Use a gray shading based on diffuse material
|
||||
};
|
||||
|
||||
#endif // _3D_ENUMS_H_
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_fastmath.cpp
|
||||
* @brief
|
||||
*/
|
||||
|
||||
|
||||
#include "3d_fastmath.h"
|
||||
|
||||
|
||||
// Fast Float Random Numbers
|
||||
// a small and fast implementation for random float numbers in the range [-1,1]
|
||||
// References : Posted by dominik.ries[AT]gmail[DOT]com
|
||||
// http://www.musicdsp.org/showone.php?id=273
|
||||
|
||||
|
||||
// set the initial seed to whatever you like
|
||||
static int s_randSeed = 1;
|
||||
|
||||
// fast rand float, using full 32bit precision
|
||||
// returns in the range [-1, 1] (not confirmed)
|
||||
float Fast_RandFloat()
|
||||
{
|
||||
s_randSeed *= 16807;
|
||||
|
||||
return (float)s_randSeed * 4.6566129e-010f;
|
||||
}
|
||||
|
||||
|
||||
// Fast rand, as described here:
|
||||
// http://wiki.osdev.org/Random_Number_Generator
|
||||
|
||||
static unsigned long int s_nextRandSeed = 1;
|
||||
|
||||
int Fast_rand( void ) // RAND_MAX assumed to be 32767
|
||||
{
|
||||
s_nextRandSeed = s_nextRandSeed * 1103515245 + 12345;
|
||||
|
||||
return (unsigned int)(s_nextRandSeed >> 16) & 0x7FFF;
|
||||
}
|
||||
|
||||
void Fast_srand( unsigned int seed )
|
||||
{
|
||||
s_nextRandSeed = seed;
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_fastmath.h
|
||||
* @brief Defines math related functions
|
||||
*/
|
||||
|
||||
#ifndef _3D_FASTMATH_H
|
||||
#define _3D_FASTMATH_H
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
// Define this flag to use fast math optimizations
|
||||
#define FASTMATH_USE
|
||||
|
||||
#define L1_CACHE_LINE_SIZE 64
|
||||
|
||||
#ifdef FASTMATH_USE
|
||||
#define INTFLOORF(s) (lrintf( (s) - (0.5f - FLT_EPSILON) ))
|
||||
#else
|
||||
#define INTFLOORF(s) ((int)( floor(s) ))
|
||||
#endif
|
||||
|
||||
|
||||
// Fast Float Random Numbers
|
||||
// a small and fast implementation for random float numbers in the range [-1,1]
|
||||
// This is not thread safe
|
||||
float Fast_RandFloat();
|
||||
|
||||
int Fast_rand( void );
|
||||
void Fast_srand( unsigned int seed );
|
||||
|
||||
/**
|
||||
* This part contains some functions from the PBRT 3 source code.
|
||||
* https://github.com/mmp/pbrt-v3/blob/master/src/core/pbrt.h
|
||||
*/
|
||||
|
||||
/*
|
||||
pbrt source code is Copyright(c) 1998-2015
|
||||
Matt Pharr, Greg Humphreys, and Wenzel Jakob.
|
||||
|
||||
This file is part of pbrt.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// Global Inline Functions
|
||||
inline uint32_t FloatToBits( float f )
|
||||
{
|
||||
uint32_t ui;
|
||||
|
||||
memcpy( &ui, &f, sizeof( float ) );
|
||||
|
||||
return ui;
|
||||
}
|
||||
|
||||
|
||||
inline float BitsToFloat( uint32_t ui )
|
||||
{
|
||||
float f;
|
||||
|
||||
memcpy( &f, &ui, sizeof (uint32_t ) );
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
inline uint64_t FloatToBits( double f )
|
||||
{
|
||||
uint64_t ui;
|
||||
|
||||
memcpy( &ui, &f, sizeof( double ) );
|
||||
|
||||
return ui;
|
||||
}
|
||||
|
||||
|
||||
inline double BitsToFloat( uint64_t ui )
|
||||
{
|
||||
double f;
|
||||
|
||||
memcpy( &f, &ui, sizeof( uint64_t ) );
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
inline float NextFloatUp( float v )
|
||||
{
|
||||
// Handle infinity and negative zero for _NextFloatUp()_
|
||||
if( std::isinf( v ) && (v > 0.) )
|
||||
return v;
|
||||
|
||||
if( v == -0.f )
|
||||
v = 0.f;
|
||||
|
||||
// Advance _v_ to next higher float
|
||||
uint32_t ui = FloatToBits( v );
|
||||
|
||||
if( v >= 0. )
|
||||
++ui;
|
||||
else
|
||||
--ui;
|
||||
|
||||
return BitsToFloat( ui );
|
||||
}
|
||||
|
||||
|
||||
inline float NextFloatDown( float v )
|
||||
{
|
||||
// Handle infinity and positive zero for _NextFloatDown()_
|
||||
if( std::isinf( v ) && (v < 0.) )
|
||||
return v;
|
||||
|
||||
if( v == 0.f )
|
||||
v = -0.f;
|
||||
|
||||
uint32_t ui = FloatToBits( v );
|
||||
|
||||
if( v > 0. )
|
||||
--ui;
|
||||
else
|
||||
++ui;
|
||||
|
||||
return BitsToFloat( ui );
|
||||
}
|
||||
|
||||
#endif // 3D_FASTMATH_H
|
||||
@@ -0,0 +1,517 @@
|
||||
/**
|
||||
* @file 3d_frame.cpp
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <appl_wxstruct.h>
|
||||
|
||||
#include <3d_viewer.h>
|
||||
#include <3d_canvas.h>
|
||||
#include <info3d_visu.h>
|
||||
#include <trackball.h>
|
||||
|
||||
#include <wx/colordlg.h>
|
||||
#include <3d_viewer_id.h>
|
||||
#include <wxBasePcbFrame.h>
|
||||
|
||||
INFO3D_VISU g_Parm_3D_Visu;
|
||||
|
||||
// Key to store 3D Viewer config:
|
||||
static const wxString keyBgColor_Red( wxT( "BgColor_Red" ) );
|
||||
static const wxString keyBgColor_Green( wxT( "BgColor_Green" ) );
|
||||
static const wxString keyBgColor_Blue( wxT( "BgColor_Blue" ) );
|
||||
static const wxString keyShowRealisticMode( wxT( "ShowRealisticMode" ) );
|
||||
static const wxString keyShowAxis( wxT( "ShowAxis" ) );
|
||||
static const wxString keyShowZones( wxT( "ShowZones" ) );
|
||||
static const wxString keyShowFootprints( wxT( "ShowFootprints" ) );
|
||||
static const wxString keyShowCopperThickness( wxT( "ShowCopperThickness" ) );
|
||||
static const wxString keyShowAdhesiveLayers( wxT( "ShowAdhesiveLayers" ) );
|
||||
static const wxString keyShowSilkScreenLayers( wxT( "ShowSilkScreenLayers" ) );
|
||||
static const wxString keyShowSolderMaskLayers( wxT( "ShowSolderMasLayers" ) );
|
||||
static const wxString keyShowSolderPasteLayers( wxT( "ShowSolderPasteLayers" ) );
|
||||
static const wxString keyShowCommentsLayer( wxT( "ShowCommentsLayers" ) );
|
||||
static const wxString keyShowBoardBody( wxT( "ShowBoardBody" ) );
|
||||
static const wxString keyShowEcoLayers( wxT( "ShowEcoLayers" ) );
|
||||
|
||||
BEGIN_EVENT_TABLE( EDA_3D_FRAME, EDA_BASE_FRAME )
|
||||
EVT_ACTIVATE( EDA_3D_FRAME::OnActivate )
|
||||
|
||||
EVT_TOOL_RANGE( ID_ZOOM_IN, ID_ZOOM_PAGE, EDA_3D_FRAME::Process_Zoom )
|
||||
EVT_TOOL_RANGE( ID_START_COMMAND_3D, ID_END_COMMAND_3D,
|
||||
EDA_3D_FRAME::Process_Special_Functions )
|
||||
EVT_TOOL( ID_TOOL_SET_VISIBLE_ITEMS, EDA_3D_FRAME::Process_Special_Functions )
|
||||
EVT_MENU( wxID_EXIT, EDA_3D_FRAME::Exit3DFrame )
|
||||
EVT_MENU( ID_MENU_SCREENCOPY_PNG, EDA_3D_FRAME::Process_Special_Functions )
|
||||
EVT_MENU( ID_MENU_SCREENCOPY_JPEG, EDA_3D_FRAME::Process_Special_Functions )
|
||||
|
||||
EVT_MENU_RANGE( ID_MENU3D_GRID, ID_MENU3D_GRID_END,
|
||||
EDA_3D_FRAME::On3DGridSelection )
|
||||
|
||||
EVT_CLOSE( EDA_3D_FRAME::OnCloseWindow )
|
||||
|
||||
END_EVENT_TABLE() EDA_3D_FRAME::EDA_3D_FRAME( PCB_BASE_FRAME* parent,
|
||||
const wxString& title,
|
||||
long style ) :
|
||||
EDA_BASE_FRAME( parent, DISPLAY3D_FRAME_TYPE, title,
|
||||
wxDefaultPosition, wxDefaultSize, style, wxT( "Frame3D" ) )
|
||||
{
|
||||
m_canvas = NULL;
|
||||
m_reloadRequest = false;
|
||||
m_ortho = false;
|
||||
|
||||
// Give it an icon
|
||||
wxIcon icon;
|
||||
icon.CopyFromBitmap( KiBitmap( icon_3d_xpm ) );
|
||||
SetIcon( icon );
|
||||
|
||||
GetSettings();
|
||||
SetSize( m_FramePos.x, m_FramePos.y, m_FrameSize.x, m_FrameSize.y );
|
||||
|
||||
// Create the status line
|
||||
static const int dims[5] = { -1, 100, 100, 100, 140 };
|
||||
|
||||
CreateStatusBar( 5 );
|
||||
SetStatusWidths( 5, dims );
|
||||
|
||||
CreateMenuBar();
|
||||
ReCreateMainToolbar();
|
||||
|
||||
// Make a EDA_3D_CANVAS
|
||||
int attrs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 };
|
||||
m_canvas = new EDA_3D_CANVAS( this, attrs );
|
||||
|
||||
m_auimgr.SetManagedWindow( this );
|
||||
|
||||
|
||||
EDA_PANEINFO horiztb;
|
||||
horiztb.HorizontalToolbarPane();
|
||||
|
||||
m_auimgr.AddPane( m_mainToolBar,
|
||||
wxAuiPaneInfo( horiztb ).Name( wxT( "m_mainToolBar" ) ).Top() );
|
||||
|
||||
m_auimgr.AddPane( m_canvas,
|
||||
wxAuiPaneInfo().Name( wxT( "DrawFrame" ) ).CentrePane() );
|
||||
|
||||
m_auimgr.Update();
|
||||
|
||||
// Fixes bug in Windows (XP and possibly others) where the canvas requires the focus
|
||||
// in order to receive mouse events. Otherwise, the user has to click somewhere on
|
||||
// the canvas before it will respond to mouse wheel events.
|
||||
m_canvas->SetFocus();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::Exit3DFrame( wxCommandEvent& event )
|
||||
{
|
||||
Close( true );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::OnCloseWindow( wxCloseEvent& Event )
|
||||
{
|
||||
if( Parent() )
|
||||
Parent()->m_Draw3DFrame = NULL;
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::GetSettings()
|
||||
{
|
||||
wxConfig* config = wxGetApp().GetSettings(); // Current config used by application
|
||||
class INFO3D_VISU& prms = g_Parm_3D_Visu;
|
||||
|
||||
if( config )
|
||||
{
|
||||
EDA_BASE_FRAME::LoadSettings();
|
||||
|
||||
config->Read( keyBgColor_Red, &g_Parm_3D_Visu.m_BgColor.m_Red, 0.0 );
|
||||
config->Read( keyBgColor_Green, &g_Parm_3D_Visu.m_BgColor.m_Green, 0.0 );
|
||||
config->Read( keyBgColor_Blue, &g_Parm_3D_Visu.m_BgColor.m_Blue, 0.0 );
|
||||
|
||||
bool tmp;
|
||||
config->Read( keyShowRealisticMode, &tmp, false );
|
||||
prms.SetFlag( FL_USE_REALISTIC_MODE, tmp );
|
||||
|
||||
config->Read( keyShowAxis, &tmp, true );
|
||||
prms.SetFlag( FL_AXIS, tmp );
|
||||
|
||||
config->Read( keyShowFootprints, &tmp, true );
|
||||
prms.SetFlag( FL_MODULE, tmp );
|
||||
|
||||
config->Read( keyShowCopperThickness, &tmp, false );
|
||||
prms.SetFlag( FL_USE_COPPER_THICKNESS, tmp );
|
||||
|
||||
config->Read( keyShowZones, &tmp, true );
|
||||
prms.SetFlag( FL_ZONE, tmp );
|
||||
|
||||
config->Read( keyShowAdhesiveLayers, &tmp, true );
|
||||
prms.SetFlag( FL_ADHESIVE, tmp );
|
||||
|
||||
config->Read( keyShowSilkScreenLayers, &tmp, true );
|
||||
prms.SetFlag( FL_SILKSCREEN, tmp );
|
||||
|
||||
config->Read( keyShowSolderMaskLayers, &tmp, true );
|
||||
prms.SetFlag( FL_SOLDERMASK, tmp );
|
||||
|
||||
config->Read( keyShowSolderPasteLayers, &tmp, true );
|
||||
prms.SetFlag( FL_SOLDERPASTE, tmp );
|
||||
|
||||
config->Read( keyShowCommentsLayer, &tmp, true );
|
||||
prms.SetFlag( FL_COMMENTS, tmp );
|
||||
|
||||
config->Read( keyShowEcoLayers, &tmp, true );
|
||||
prms.SetFlag( FL_ECO, tmp );
|
||||
|
||||
config->Read( keyShowBoardBody, &tmp, true );
|
||||
prms.SetFlag( FL_SHOW_BOARD_BODY, tmp );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::SaveSettings()
|
||||
{
|
||||
wxConfig* config = wxGetApp().GetSettings(); // Current config used by application
|
||||
|
||||
if( !config )
|
||||
return;
|
||||
|
||||
EDA_BASE_FRAME::SaveSettings();
|
||||
|
||||
config->Write( keyBgColor_Red, g_Parm_3D_Visu.m_BgColor.m_Red );
|
||||
config->Write( keyBgColor_Green, g_Parm_3D_Visu.m_BgColor.m_Green );
|
||||
config->Write( keyBgColor_Blue, g_Parm_3D_Visu.m_BgColor.m_Blue );
|
||||
class INFO3D_VISU& prms = g_Parm_3D_Visu;
|
||||
config->Write( keyShowRealisticMode, prms.GetFlag( FL_USE_REALISTIC_MODE ) );
|
||||
config->Write( keyShowAxis, prms.GetFlag( FL_AXIS ) );
|
||||
config->Write( keyShowFootprints, prms.GetFlag( FL_MODULE ) );
|
||||
config->Write( keyShowCopperThickness, prms.GetFlag( FL_USE_COPPER_THICKNESS ) );
|
||||
config->Write( keyShowZones, prms.GetFlag( FL_ZONE ) );
|
||||
config->Write( keyShowAdhesiveLayers, prms.GetFlag( FL_ADHESIVE ) );
|
||||
config->Write( keyShowSilkScreenLayers, prms.GetFlag( FL_SILKSCREEN ) );
|
||||
config->Write( keyShowSolderMaskLayers, prms.GetFlag( FL_SOLDERMASK ) );
|
||||
config->Write( keyShowSolderPasteLayers, prms.GetFlag( FL_SOLDERPASTE ) );
|
||||
config->Write( keyShowCommentsLayer, prms.GetFlag( FL_COMMENTS ) );
|
||||
config->Write( keyShowEcoLayers, prms.GetFlag( FL_ECO ) );
|
||||
config->Write( keyShowBoardBody, prms.GetFlag( FL_SHOW_BOARD_BODY ) );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::Process_Zoom( wxCommandEvent& event )
|
||||
{
|
||||
int ii;
|
||||
|
||||
switch( event.GetId() )
|
||||
{
|
||||
case ID_ZOOM_PAGE:
|
||||
|
||||
for( ii = 0; ii < 4; ii++ )
|
||||
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
|
||||
|
||||
g_Parm_3D_Visu.m_Zoom = 1.0;
|
||||
m_canvas->SetOffset( 0.0, 0.0 );
|
||||
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
|
||||
break;
|
||||
|
||||
case ID_ZOOM_IN:
|
||||
g_Parm_3D_Visu.m_Zoom /= 1.2;
|
||||
|
||||
if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
|
||||
g_Parm_3D_Visu.m_Zoom = 0.01;
|
||||
|
||||
break;
|
||||
|
||||
case ID_ZOOM_OUT:
|
||||
g_Parm_3D_Visu.m_Zoom *= 1.2;
|
||||
break;
|
||||
|
||||
case ID_ZOOM_REDRAW:
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
m_canvas->Refresh( false );
|
||||
m_canvas->DisplayStatus();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMenu )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
double EDA_3D_FRAME::BestZoom()
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::RedrawActiveWindow( wxDC* DC, bool EraseBg )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::Process_Special_Functions( wxCommandEvent& event )
|
||||
{
|
||||
#define ROT_ANGLE 10.0
|
||||
int id = event.GetId();
|
||||
bool isChecked = event.IsChecked();
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case ID_TOOL_SET_VISIBLE_ITEMS:
|
||||
Install_3D_ViewOptionDialog( event );
|
||||
break;
|
||||
|
||||
case ID_RELOAD3D_BOARD:
|
||||
NewDisplay();
|
||||
return;
|
||||
break;
|
||||
|
||||
case ID_ROTATE3D_X_POS:
|
||||
g_Parm_3D_Visu.m_ROTX += ROT_ANGLE;
|
||||
break;
|
||||
|
||||
case ID_ROTATE3D_X_NEG:
|
||||
g_Parm_3D_Visu.m_ROTX -= ROT_ANGLE;
|
||||
break;
|
||||
|
||||
case ID_ROTATE3D_Y_POS:
|
||||
g_Parm_3D_Visu.m_ROTY += ROT_ANGLE;
|
||||
break;
|
||||
|
||||
case ID_ROTATE3D_Y_NEG:
|
||||
g_Parm_3D_Visu.m_ROTY -= ROT_ANGLE;
|
||||
break;
|
||||
|
||||
case ID_ROTATE3D_Z_POS:
|
||||
g_Parm_3D_Visu.m_ROTZ += ROT_ANGLE;
|
||||
break;
|
||||
|
||||
case ID_ROTATE3D_Z_NEG:
|
||||
g_Parm_3D_Visu.m_ROTZ -= ROT_ANGLE;
|
||||
break;
|
||||
|
||||
case ID_MOVE3D_LEFT:
|
||||
m_canvas->SetView3D( WXK_LEFT );
|
||||
return;
|
||||
|
||||
case ID_MOVE3D_RIGHT:
|
||||
m_canvas->SetView3D( WXK_RIGHT );
|
||||
return;
|
||||
|
||||
case ID_MOVE3D_UP:
|
||||
m_canvas->SetView3D( WXK_UP );
|
||||
return;
|
||||
|
||||
case ID_MOVE3D_DOWN:
|
||||
m_canvas->SetView3D( WXK_DOWN );
|
||||
return;
|
||||
|
||||
case ID_ORTHO:
|
||||
ToggleOrtho();
|
||||
return;
|
||||
|
||||
case ID_TOOL_SCREENCOPY_TOCLIBBOARD:
|
||||
case ID_MENU_SCREENCOPY_PNG:
|
||||
case ID_MENU_SCREENCOPY_JPEG:
|
||||
m_canvas->TakeScreenshot( event );
|
||||
break;
|
||||
|
||||
case ID_MENU3D_BGCOLOR_SELECTION:
|
||||
Set3DBgColor();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_REALISTIC_MODE:
|
||||
g_Parm_3D_Visu.SetFlag( FL_USE_REALISTIC_MODE, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_SHOW_BOARD_BODY:
|
||||
g_Parm_3D_Visu.SetFlag( FL_SHOW_BOARD_BODY, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_AXIS_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_AXIS, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_MODULE_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_MODULE, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_USE_COPPER_THICKNESS:
|
||||
g_Parm_3D_Visu.SetFlag( FL_USE_COPPER_THICKNESS, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_ZONE_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_ZONE, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_ADHESIVE_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_ADHESIVE, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_SILKSCREEN_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_SILKSCREEN, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_SOLDER_MASK_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_SOLDERMASK, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_SOLDER_PASTE_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_SOLDERPASTE, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_COMMENTS_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_COMMENTS, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
case ID_MENU3D_ECO_ONOFF:
|
||||
g_Parm_3D_Visu.SetFlag( FL_ECO, isChecked );
|
||||
NewDisplay();
|
||||
return;
|
||||
|
||||
default:
|
||||
wxLogMessage( wxT( "EDA_3D_FRAME::Process_Special_Functions() error: unknown command" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
m_canvas->Refresh( true );
|
||||
m_canvas->DisplayStatus();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::On3DGridSelection( wxCommandEvent& event )
|
||||
{
|
||||
int id = event.GetId();
|
||||
|
||||
for( int ii = ID_MENU3D_GRID; ii < ID_MENU3D_GRID_END; ii++ )
|
||||
{
|
||||
if( event.GetId() == ii )
|
||||
continue;
|
||||
|
||||
GetMenuBar()->Check( ii, false );
|
||||
}
|
||||
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case ID_MENU3D_GRID_NOGRID:
|
||||
g_Parm_3D_Visu.SetFlag( FL_GRID, false );
|
||||
break;
|
||||
|
||||
case ID_MENU3D_GRID_10_MM:
|
||||
g_Parm_3D_Visu.SetFlag( FL_GRID, true );
|
||||
g_Parm_3D_Visu.m_3D_Grid = 10.0;
|
||||
break;
|
||||
|
||||
case ID_MENU3D_GRID_5_MM:
|
||||
g_Parm_3D_Visu.SetFlag( FL_GRID, true );
|
||||
g_Parm_3D_Visu.m_3D_Grid = 5.0;
|
||||
break;
|
||||
|
||||
case ID_MENU3D_GRID_2P5_MM:
|
||||
g_Parm_3D_Visu.SetFlag( FL_GRID, true );
|
||||
g_Parm_3D_Visu.m_3D_Grid = 2.5;
|
||||
break;
|
||||
|
||||
case ID_MENU3D_GRID_1_MM:
|
||||
g_Parm_3D_Visu.SetFlag( FL_GRID, true );
|
||||
g_Parm_3D_Visu.m_3D_Grid = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogMessage( wxT( "EDA_3D_FRAME::On3DGridSelection() error: unknown command" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
NewDisplay();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::NewDisplay()
|
||||
{
|
||||
m_reloadRequest = false;
|
||||
|
||||
m_canvas->ClearLists();
|
||||
m_canvas->CreateDrawGL_List();
|
||||
|
||||
// m_canvas->InitGL();
|
||||
m_canvas->Refresh( true );
|
||||
m_canvas->DisplayStatus();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_FRAME::OnActivate( wxActivateEvent& event )
|
||||
{
|
||||
// Reload data if 3D frame shows a footprint,
|
||||
// because it can be changed since last frame activation
|
||||
if( m_reloadRequest )
|
||||
NewDisplay();
|
||||
|
||||
event.Skip(); // required under wxMAC
|
||||
}
|
||||
|
||||
|
||||
/* called to set the background color of the 3D scene
|
||||
*/
|
||||
void EDA_3D_FRAME::Set3DBgColor()
|
||||
{
|
||||
S3D_COLOR color;
|
||||
wxColour newcolor, oldcolor;
|
||||
|
||||
oldcolor.Set( KiROUND( g_Parm_3D_Visu.m_BgColor.m_Red * 255 ),
|
||||
KiROUND( g_Parm_3D_Visu.m_BgColor.m_Green * 255 ),
|
||||
KiROUND( g_Parm_3D_Visu.m_BgColor.m_Blue * 255 ) );
|
||||
|
||||
newcolor = wxGetColourFromUser( this, oldcolor );
|
||||
|
||||
if( newcolor != oldcolor )
|
||||
{
|
||||
g_Parm_3D_Visu.m_BgColor.m_Red = (double) newcolor.Red() / 255.0;
|
||||
g_Parm_3D_Visu.m_BgColor.m_Green = (double) newcolor.Green() / 255.0;
|
||||
g_Parm_3D_Visu.m_BgColor.m_Blue = (double) newcolor.Blue() / 255.0;
|
||||
NewDisplay();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_math.cpp
|
||||
* @brief
|
||||
*/
|
||||
|
||||
|
||||
#include "3d_math.h"
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_math.h
|
||||
* @brief Defines math related functions
|
||||
*/
|
||||
|
||||
#ifndef _3D_MATH_H
|
||||
#define _3D_MATH_H
|
||||
|
||||
#include <plugins/3dapi/xv3d_types.h>
|
||||
#include "3d_fastmath.h"
|
||||
|
||||
/**
|
||||
* https://en.wikipedia.org/wiki/Spherical_coordinate_system
|
||||
*
|
||||
* @param aInclination θ ∈ [0, π]
|
||||
* @param aAzimuth φ ∈ [0, 2π]
|
||||
* @return Cartesian coordinates
|
||||
*/
|
||||
inline SFVEC3F SphericalToCartesian( float aInclination, float aAzimuth )
|
||||
{
|
||||
float sinInc = glm::sin( aInclination );
|
||||
|
||||
return SFVEC3F( sinInc * glm::cos( aAzimuth ), sinInc * glm::sin( aAzimuth ),
|
||||
glm::cos( aInclination ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo This is not correct because it is not a gaussian random.
|
||||
*/
|
||||
inline SFVEC3F UniformRandomHemisphereDirection()
|
||||
{
|
||||
// It was experienced that this function is slow! do not use it :/
|
||||
// SFVEC3F b( (rand()/(float)RAND_MAX) - 0.5f,
|
||||
// (rand()/(float)RAND_MAX) - 0.5f,
|
||||
// (rand()/(float)RAND_MAX) - 0.5f );
|
||||
|
||||
SFVEC3F b( Fast_RandFloat() * 0.5f, Fast_RandFloat() * 0.5f, Fast_RandFloat() * 0.5f );
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
// https://pathtracing.wordpress.com/2011/03/03/cosine-weighted-hemisphere/
|
||||
inline SFVEC3F CosWeightedRandomHemisphereDirection( const SFVEC3F& n )
|
||||
{
|
||||
const float Xi1 = (float) rand() / (float) RAND_MAX;
|
||||
const float Xi2 = (float) rand() / (float) RAND_MAX;
|
||||
|
||||
const float theta = acos( sqrt( 1.0f - Xi1 ) );
|
||||
const float phi = 2.0f * glm::pi<float>() * Xi2;
|
||||
|
||||
const float xs = sinf( theta ) * cosf( phi );
|
||||
const float ys = cosf( theta );
|
||||
const float zs = sinf( theta ) * sinf( phi );
|
||||
|
||||
const SFVEC3F y( n.x, n.y, n.z );
|
||||
SFVEC3F h = y;
|
||||
|
||||
if( fabs( h.x ) <= fabs( h.y ) && fabs( h.x ) <= fabs( h.z ) )
|
||||
h.x= 1.0f;
|
||||
else if( fabs( h.y ) <= fabs( h.x ) && fabs( h.y ) <= fabs( h.z ) )
|
||||
h.y= 1.0f;
|
||||
else
|
||||
h.z= 1.0f;
|
||||
|
||||
|
||||
const SFVEC3F x = glm::normalize( glm::cross( h, y ) );
|
||||
const SFVEC3F z = glm::normalize( glm::cross( x, y ) );
|
||||
|
||||
SFVEC3F direction = xs * x + ys * y + zs * z;
|
||||
return glm::normalize( direction );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Based on:
|
||||
* https://github.com/mmp/pbrt-v3/blob/master/src/core/reflection.h
|
||||
* See also:
|
||||
* http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_3_Refractions_and_Beers_Law.shtml
|
||||
*
|
||||
* @param aInVector incoming vector.
|
||||
* @param aNormal normal in the intersection point.
|
||||
* @param aRin_over_Rout incoming refraction index / out refraction index.
|
||||
* @param aOutVector the refracted vector.
|
||||
* @return true
|
||||
*/
|
||||
inline bool Refract( const SFVEC3F &aInVector, const SFVEC3F &aNormal, float aRin_over_Rout,
|
||||
SFVEC3F& aOutVector )
|
||||
{
|
||||
float cosThetaI = -glm::dot( aNormal, aInVector );
|
||||
float sin2ThetaI = glm::max( 0.0f, 1.0f - cosThetaI * cosThetaI );
|
||||
float sin2ThetaT = aRin_over_Rout * aRin_over_Rout * sin2ThetaI;
|
||||
|
||||
// Handle total internal reflection for transmission
|
||||
if( sin2ThetaT >= 1.0f )
|
||||
return false;
|
||||
|
||||
float cosThetaT = sqrtf( 1.0f - sin2ThetaT );
|
||||
|
||||
aOutVector = glm::normalize( aRin_over_Rout * aInVector +
|
||||
( aRin_over_Rout * cosThetaI - cosThetaT ) *
|
||||
aNormal );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline float mapf( float x, float in_min, float in_max, float out_min, float out_max )
|
||||
{
|
||||
x = glm::clamp( x, in_min, in_max );
|
||||
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
inline float RGBtoGray( const SFVEC3F &aColor )
|
||||
{
|
||||
return (aColor.r * 0.2126f +
|
||||
aColor.g * 0.7152f +
|
||||
aColor.b * 0.0722f);
|
||||
}
|
||||
|
||||
inline SFVEC3F MaterialDiffuseToColorCAD( const SFVEC3F &aDiffuseColor )
|
||||
{
|
||||
// convert to a discret scale of grays
|
||||
const float luminance = glm::min(
|
||||
( ( (float) ( (unsigned int) ( 4.0f * RGBtoGray( aDiffuseColor ) ) ) + 0.5f ) / 4.0f )
|
||||
* 1.0f,
|
||||
1.0f );
|
||||
|
||||
const float maxValue = glm::max( glm::max( glm::max( aDiffuseColor.r, aDiffuseColor.g ),
|
||||
aDiffuseColor.b ), FLT_EPSILON );
|
||||
|
||||
return ( aDiffuseColor / SFVEC3F( maxValue ) ) * 0.125f + luminance * 0.875f;
|
||||
}
|
||||
|
||||
|
||||
// http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJ4KngqMiIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MCwiZXEiOiItKCh4LTEpXjIpKjIrMSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0xLjM4NzUwMDAwMDAwMDAwMDIiLCIxLjg2MjQ5OTk5OTk5OTk5OTgiLCItMC43IiwiMS4zIl19XQ--
|
||||
inline float QuadricEasingInOut( float t )
|
||||
{
|
||||
if( t <= 0.5f )
|
||||
{
|
||||
return t * t * 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = t - 1.0f;
|
||||
|
||||
return -2.0f * ( t * t ) + 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// http://www.wolframalpha.com/input/?i=t%5E2(3-2t)
|
||||
inline float BezierBlend( float t )
|
||||
{
|
||||
return t * t * ( 3.0f - 2.0f * t );
|
||||
}
|
||||
|
||||
#endif // 3D_MATH_H
|
||||
@@ -1,466 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Implements a model viewer canvas. The purpose of the model viewer is to render
|
||||
* 3d models that come in the original data from the files without any transformations.
|
||||
*/
|
||||
|
||||
#include <gal/opengl/kiglew.h> // Must be included first
|
||||
#include <iostream>
|
||||
#include "3d_rendering/opengl/3d_model.h"
|
||||
#include "eda_3d_model_viewer.h"
|
||||
#include "../3d_rendering/opengl/opengl_utils.h"
|
||||
#include "../3d_cache/3d_cache.h"
|
||||
#include <wx/dcclient.h>
|
||||
#include <base_units.h>
|
||||
#include <gl_context_mgr.h>
|
||||
#include <settings/common_settings.h>
|
||||
#include <pgm_base.h>
|
||||
#include <gal/dpi_scaling.h>
|
||||
#include <macros.h>
|
||||
|
||||
/**
|
||||
* Scale conversion from 3d model units to pcb units
|
||||
*/
|
||||
#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable the trace output of this class.
|
||||
* The debug output can be turned on by setting the WXTRACE environment variable to
|
||||
* "KI_TRACE_EDA_3D_MODEL_VIEWER". See the wxWidgets documentation on wxLogTrace for
|
||||
* more information.
|
||||
*
|
||||
* @ingroup trace_env_vars
|
||||
*/
|
||||
const wxChar* EDA_3D_MODEL_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_MODEL_VIEWER" );
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE( EDA_3D_MODEL_VIEWER, wxGLCanvas )
|
||||
EVT_PAINT( EDA_3D_MODEL_VIEWER::OnPaint )
|
||||
|
||||
// mouse events
|
||||
EVT_LEFT_DOWN( EDA_3D_MODEL_VIEWER::OnLeftDown )
|
||||
EVT_LEFT_UP( EDA_3D_MODEL_VIEWER::OnLeftUp )
|
||||
EVT_MIDDLE_UP( EDA_3D_MODEL_VIEWER::OnMiddleUp )
|
||||
EVT_MIDDLE_DOWN( EDA_3D_MODEL_VIEWER::OnMiddleDown)
|
||||
EVT_RIGHT_DOWN( EDA_3D_MODEL_VIEWER::OnRightClick )
|
||||
EVT_MOUSEWHEEL( EDA_3D_MODEL_VIEWER::OnMouseWheel )
|
||||
EVT_MOTION( EDA_3D_MODEL_VIEWER::OnMouseMove )
|
||||
|
||||
#ifdef USE_OSX_MAGNIFY_EVENT
|
||||
EVT_MAGNIFY( EDA_3D_MODEL_VIEWER::OnMagnify )
|
||||
#endif
|
||||
|
||||
// other events
|
||||
EVT_ERASE_BACKGROUND( EDA_3D_MODEL_VIEWER::OnEraseBackground )
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
// This defines the range that all coord will have to be rendered.
|
||||
// It will use this value to convert to a normalized value between
|
||||
// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
|
||||
#define RANGE_SCALE_3D 8.0f
|
||||
|
||||
|
||||
EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER( wxWindow* aParent, const int* aAttribList,
|
||||
S3D_CACHE* aCacheManager ) :
|
||||
HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
|
||||
wxFULL_REPAINT_ON_RESIZE ),
|
||||
m_trackBallCamera( RANGE_SCALE_3D * 4.0f ),
|
||||
m_cacheManager( aCacheManager )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER" ) );
|
||||
|
||||
m_ogl_initialized = false;
|
||||
m_reload_is_needed = false;
|
||||
m_ogl_3dmodel = nullptr;
|
||||
m_3d_model = nullptr;
|
||||
m_BiuTo3dUnits = 1.0;
|
||||
|
||||
m_glRC = nullptr;
|
||||
|
||||
COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
|
||||
|
||||
const DPI_SCALING dpi{ settings, this };
|
||||
SetScaleFactor( dpi.GetScaleFactor() );
|
||||
}
|
||||
|
||||
|
||||
EDA_3D_MODEL_VIEWER::~EDA_3D_MODEL_VIEWER()
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::~EDA_3D_MODEL_VIEWER" ) );
|
||||
|
||||
if( m_glRC )
|
||||
{
|
||||
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this );
|
||||
|
||||
delete m_ogl_3dmodel;
|
||||
m_ogl_3dmodel = nullptr;
|
||||
|
||||
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
|
||||
GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::Set3DModel( const S3DMODEL& a3DModel )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
|
||||
|
||||
// Validate a3DModel pointers
|
||||
wxASSERT( a3DModel.m_Materials != nullptr );
|
||||
wxASSERT( a3DModel.m_Meshes != nullptr );
|
||||
wxASSERT( a3DModel.m_MaterialsSize > 0 );
|
||||
wxASSERT( a3DModel.m_MeshesSize > 0 );
|
||||
|
||||
// Delete the old model
|
||||
delete m_ogl_3dmodel;
|
||||
m_ogl_3dmodel = nullptr;
|
||||
|
||||
m_3d_model = nullptr;
|
||||
|
||||
if( ( a3DModel.m_Materials != nullptr ) && ( a3DModel.m_Meshes != nullptr )
|
||||
&& ( a3DModel.m_MaterialsSize > 0 ) && ( a3DModel.m_MeshesSize > 0 ) )
|
||||
{
|
||||
m_3d_model = &a3DModel;
|
||||
m_reload_is_needed = true;
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
|
||||
|
||||
if( m_cacheManager )
|
||||
{
|
||||
const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString );
|
||||
|
||||
if( model )
|
||||
Set3DModel( (const S3DMODEL &)*model );
|
||||
else
|
||||
Clear3DModel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::Clear3DModel()
|
||||
{
|
||||
// Delete the old model
|
||||
m_reload_is_needed = false;
|
||||
|
||||
delete m_ogl_3dmodel;
|
||||
m_ogl_3dmodel = nullptr;
|
||||
|
||||
m_3d_model = nullptr;
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::ogl_initialize()
|
||||
{
|
||||
const GLenum err = glewInit();
|
||||
|
||||
if( GLEW_OK != err )
|
||||
{
|
||||
const wxString msgError = (const char*) glewGetErrorString( err );
|
||||
|
||||
wxLogMessage( msgError );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::ogl_initialize Using GLEW version %s" ),
|
||||
FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
|
||||
}
|
||||
|
||||
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
|
||||
glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
|
||||
glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
|
||||
|
||||
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
||||
glEnable( GL_DEPTH_TEST );
|
||||
glEnable( GL_CULL_FACE );
|
||||
glShadeModel( GL_SMOOTH );
|
||||
glEnable( GL_LINE_SMOOTH );
|
||||
glEnable( GL_NORMALIZE );
|
||||
|
||||
// Setup light
|
||||
// https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
|
||||
const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
|
||||
const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
// defines a directional light that points along the negative z-axis
|
||||
const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
|
||||
|
||||
const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
|
||||
glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
|
||||
glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
|
||||
glLightfv( GL_LIGHT0, GL_POSITION, position );
|
||||
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::ogl_set_arrow_material()
|
||||
{
|
||||
glEnable( GL_COLOR_MATERIAL );
|
||||
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
||||
|
||||
const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
|
||||
|
||||
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
|
||||
glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
|
||||
{
|
||||
event.Skip( false );
|
||||
|
||||
// SwapBuffer requires the window to be shown before calling
|
||||
if( !IsShownOnScreen() )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint !IsShown" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// "Makes the OpenGL state that is represented by the OpenGL rendering
|
||||
// context context current, i.e. it will be used by all subsequent OpenGL calls.
|
||||
// This function may only be called when the window is shown on screen"
|
||||
if( m_glRC == nullptr )
|
||||
m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this );
|
||||
|
||||
GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this );
|
||||
|
||||
// Set the OpenGL viewport according to the client size of this canvas.
|
||||
// This is done here rather than in a wxSizeEvent handler because our
|
||||
// OpenGL rendering context (and thus viewport setting) is used with
|
||||
// multiple canvases: If we updated the viewport in the wxSizeEvent
|
||||
// handler, changing the size of one canvas causes a viewport setting that
|
||||
// is wrong when next another canvas is repainted.
|
||||
wxSize clientSize = GetNativePixelSize();
|
||||
|
||||
if( !m_ogl_initialized )
|
||||
{
|
||||
m_ogl_initialized = true;
|
||||
ogl_initialize();
|
||||
}
|
||||
|
||||
if( m_reload_is_needed )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
|
||||
|
||||
m_reload_is_needed = false;
|
||||
m_ogl_3dmodel = new MODEL_3D( *m_3d_model, MATERIAL_MODE::NORMAL );
|
||||
|
||||
// It convert a model as it was a board, so get the max size dimension of the board
|
||||
// and compute the conversion scale
|
||||
m_BiuTo3dUnits =
|
||||
(double) RANGE_SCALE_3D
|
||||
/ ( (double) m_ogl_3dmodel->GetBBox().GetMaxDimension() * UNITS3D_TO_UNITSPCB );
|
||||
}
|
||||
|
||||
glViewport( 0, 0, clientSize.x, clientSize.y );
|
||||
|
||||
m_trackBallCamera.SetCurWindowSize( clientSize );
|
||||
|
||||
// clear color and depth buffers
|
||||
glEnable( GL_DEPTH_TEST );
|
||||
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
|
||||
glClearDepth( 1.0f );
|
||||
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||
|
||||
// Set projection and modelview matrices
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
|
||||
glEnable( GL_LIGHTING );
|
||||
glEnable( GL_LIGHT0 );
|
||||
|
||||
// Render Model
|
||||
if( m_ogl_3dmodel )
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
|
||||
|
||||
glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
|
||||
modelunit_to_3d_units_factor );
|
||||
|
||||
// Center model in the render viewport
|
||||
const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
|
||||
|
||||
glTranslatef( -model_center.x, -model_center.y, -model_center.z );
|
||||
|
||||
m_ogl_3dmodel->BeginDrawMulti( true );
|
||||
|
||||
m_ogl_3dmodel->DrawOpaque( false );
|
||||
m_ogl_3dmodel->DrawTransparent( 1.0f, false );
|
||||
|
||||
m_ogl_3dmodel->EndDrawMulti();
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// YxY squared view port
|
||||
glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
|
||||
glClear( GL_DEPTH_BUFFER_BIT );
|
||||
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadIdentity();
|
||||
gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
|
||||
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
|
||||
const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
|
||||
SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
|
||||
|
||||
const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
|
||||
|
||||
glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
|
||||
|
||||
ogl_set_arrow_material();
|
||||
|
||||
glColor3f( 0.9f, 0.0f, 0.0f );
|
||||
DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
|
||||
0.275f );
|
||||
|
||||
glColor3f( 0.0f, 0.9f, 0.0f );
|
||||
DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
|
||||
0.275f );
|
||||
|
||||
glColor3f( 0.0f, 0.0f, 0.9f );
|
||||
DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
|
||||
0.275f );
|
||||
|
||||
// "Swaps the double-buffer of this window, making the back-buffer the
|
||||
// front-buffer and vice versa, so that the output of the previous OpenGL
|
||||
// commands is displayed on the window."
|
||||
SwapBuffers();
|
||||
|
||||
GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnEraseBackground" ) );
|
||||
// Do nothing, to avoid flashing.
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnMouseWheel" ) );
|
||||
|
||||
if( event.ShiftDown() )
|
||||
{
|
||||
//if( event.GetWheelRotation() < 0 )
|
||||
//SetView3D( WXK_UP ); // move up
|
||||
//else
|
||||
//SetView3D( WXK_DOWN ); // move down
|
||||
}
|
||||
else if( event.ControlDown() )
|
||||
{
|
||||
//if( event.GetWheelRotation() > 0 )
|
||||
//SetView3D( WXK_RIGHT ); // move right
|
||||
//else
|
||||
//SetView3D( WXK_LEFT ); // move left
|
||||
}
|
||||
else
|
||||
{
|
||||
m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
|
||||
|
||||
//DisplayStatus();
|
||||
Refresh( false );
|
||||
}
|
||||
|
||||
m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_OSX_MAGNIFY_EVENT
|
||||
void EDA_3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
|
||||
{
|
||||
const wxSize& nativeWinSize = GetNativePixelSize();
|
||||
const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
|
||||
|
||||
m_trackBallCamera.SetCurWindowSize( nativeWinSize );
|
||||
|
||||
if( event.Dragging() )
|
||||
{
|
||||
if( event.LeftIsDown() ) // Drag
|
||||
m_trackBallCamera.Drag( nativePosition );
|
||||
|
||||
// orientation has changed, redraw mesh
|
||||
Refresh( false );
|
||||
}
|
||||
|
||||
m_trackBallCamera.SetCurMousePosition( nativePosition );
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void EDA_3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
|
||||
{
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file c3d_model_viewer.h
|
||||
* @brief Implements a model viewer canvas.
|
||||
*
|
||||
* The purpose of model viewer is to render 3d models that come in the original data from
|
||||
* the files without any transformations.
|
||||
*/
|
||||
|
||||
#ifndef _C3D_MODEL_VIEWER_H_
|
||||
#define _C3D_MODEL_VIEWER_H_
|
||||
|
||||
#include "3d_rendering/track_ball.h"
|
||||
#include <gal/hidpi_gl_canvas.h>
|
||||
|
||||
class S3D_CACHE;
|
||||
class MODEL_3D;
|
||||
|
||||
/**
|
||||
* Implement a canvas based on a wxGLCanvas.
|
||||
*/
|
||||
class EDA_3D_MODEL_VIEWER : public HIDPI_GL_CANVAS
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a new 3D Canvas with a attribute list.
|
||||
*
|
||||
* @param aParent the parent creator of this canvas.
|
||||
* @param aAttribList a list of openGL options created by #OGL_ATT_LIST::GetAttributesList.
|
||||
*/
|
||||
EDA_3D_MODEL_VIEWER( wxWindow* aParent, const int* aAttribList = nullptr,
|
||||
S3D_CACHE* aCacheManager = nullptr );
|
||||
|
||||
~EDA_3D_MODEL_VIEWER();
|
||||
|
||||
/**
|
||||
* Set this model to be displayed.
|
||||
*
|
||||
* @param a3DModel 3D model data.
|
||||
*/
|
||||
void Set3DModel( const S3DMODEL& a3DModel );
|
||||
|
||||
/**
|
||||
* Set this model to be displayed.
|
||||
*
|
||||
* @param aModelPathName 3D model path name.
|
||||
*/
|
||||
void Set3DModel( const wxString& aModelPathName );
|
||||
|
||||
/**
|
||||
* Unload the displayed 3D model.
|
||||
*/
|
||||
void Clear3DModel();
|
||||
|
||||
private:
|
||||
void ogl_initialize();
|
||||
void ogl_set_arrow_material();
|
||||
|
||||
void OnPaint( wxPaintEvent& event );
|
||||
|
||||
void OnEraseBackground( wxEraseEvent& event );
|
||||
|
||||
void OnMouseWheel( wxMouseEvent& event );
|
||||
|
||||
#ifdef USE_OSX_MAGNIFY_EVENT
|
||||
void OnMagnify( wxMouseEvent& event );
|
||||
#endif
|
||||
|
||||
void OnMouseMove( wxMouseEvent& event );
|
||||
|
||||
void OnLeftDown( wxMouseEvent& event );
|
||||
|
||||
void OnLeftUp( wxMouseEvent& event );
|
||||
|
||||
void OnMiddleUp( wxMouseEvent& event );
|
||||
|
||||
void OnMiddleDown( wxMouseEvent& event );
|
||||
|
||||
void OnRightClick( wxMouseEvent& event );
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
/// openGL context
|
||||
wxGLContext* m_glRC;
|
||||
|
||||
/// Camera used in this canvas
|
||||
TRACK_BALL m_trackBallCamera;
|
||||
|
||||
/// Original 3d model data
|
||||
const S3DMODEL* m_3d_model;
|
||||
|
||||
/// Class holder for 3d model to display on openGL
|
||||
MODEL_3D* m_ogl_3dmodel;
|
||||
|
||||
/// Flag that we have a new model and it need to be reloaded when the paint is called
|
||||
bool m_reload_is_needed;
|
||||
|
||||
/// Flag if open gl was initialized
|
||||
bool m_ogl_initialized;
|
||||
|
||||
/// factor to convert the model or any other items to keep it in relation to
|
||||
/// the +/-RANGE_SCALE_3D
|
||||
/// (it is named same as the board render for better understanding proposes)
|
||||
double m_BiuTo3dUnits;
|
||||
|
||||
/// Optional cache manager
|
||||
S3D_CACHE* m_cacheManager;
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable the trace output of this class.
|
||||
* The debug output can be turned on by setting the WXTRACE environment variable to
|
||||
* "KI_TRACE_EDA_3D_MODEL_VIEWER". See the wxWidgets documentation on wxLogTrace for
|
||||
* more information.
|
||||
*/
|
||||
static const wxChar* m_logTrace;
|
||||
};
|
||||
|
||||
#endif // _C3D_MODEL_VIEWER_H_
|
||||
@@ -1,31 +0,0 @@
|
||||
if( KICAD_USE_3DCONNEXION )
|
||||
add_library(3d-viewer_navlib STATIC
|
||||
"nl_3d_viewer_plugin.cpp"
|
||||
"nl_3d_viewer_plugin_impl.cpp"
|
||||
)
|
||||
|
||||
# 3d-viewer_navlib depends on make_lexer outputs in common
|
||||
add_dependencies( 3d-viewer_navlib pcbcommon )
|
||||
|
||||
# Find the 3DxWare SDK component 3DxWare::NlClient
|
||||
# find_package(TDxWare_SDK 4.0 REQUIRED COMPONENTS 3DxWare::Navlib)
|
||||
target_compile_definitions(3d-viewer_navlib PRIVATE
|
||||
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_DEFINITIONS>
|
||||
)
|
||||
target_compile_options(3d-viewer_navlib PRIVATE
|
||||
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_OPTIONS>
|
||||
)
|
||||
target_include_directories(3d-viewer_navlib PRIVATE
|
||||
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:pcbnew_kiface_objects,INCLUDE_DIRECTORIES>
|
||||
)
|
||||
target_link_libraries(3d-viewer_navlib
|
||||
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_LINK_LIBRARIES>
|
||||
3DxWare::Navlib
|
||||
)
|
||||
else()
|
||||
add_library(3d-viewer_navlib STATIC
|
||||
"nl_3d_viewer_plugin.cpp"
|
||||
)
|
||||
endif(KICAD_USE_3DCONNEXION)
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021 3Dconnexion
|
||||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nl_3d_viewer_plugin.h"
|
||||
|
||||
#if defined( KICAD_USE_3DCONNEXION )
|
||||
#include "nl_3d_viewer_plugin_impl.h"
|
||||
|
||||
|
||||
NL_3D_VIEWER_PLUGIN::NL_3D_VIEWER_PLUGIN( EDA_3D_CANVAS* aViewport ) :
|
||||
m_impl( new NL_3D_VIEWER_PLUGIN_IMPL( aViewport ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NL_3D_VIEWER_PLUGIN::~NL_3D_VIEWER_PLUGIN()
|
||||
{
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
|
||||
void NL_3D_VIEWER_PLUGIN::SetFocus( bool focus )
|
||||
{
|
||||
m_impl->SetFocus( focus );
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
NL_3D_VIEWER_PLUGIN::NL_3D_VIEWER_PLUGIN( EDA_3D_CANVAS* aViewport ) : m_impl( nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void NL_3D_VIEWER_PLUGIN::SetFocus( bool focus )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NL_3D_VIEWER_PLUGIN::~NL_3D_VIEWER_PLUGIN()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021 3Dconnexion
|
||||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file nl_3d_viewer_plugin.h
|
||||
* @brief Declaration of the NL_3D_VIEWER_PLUGIN class
|
||||
*/
|
||||
|
||||
#ifndef NL_3D_VIEWER_PLUGIN_H_
|
||||
#define NL_3D_VIEWER_PLUGIN_H_
|
||||
|
||||
// Forward declarations.
|
||||
class EDA_3D_CANVAS;
|
||||
class NL_3D_VIEWER_PLUGIN_IMPL;
|
||||
|
||||
/**
|
||||
* The class that implements the public interface to the SpaceMouse plug-in.
|
||||
*/
|
||||
class NL_3D_VIEWER_PLUGIN
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes a new instance of the NL_3D_VIEWER_PLUGIN.
|
||||
*
|
||||
* @param aViewport is the viewport to be navigated.
|
||||
*/
|
||||
NL_3D_VIEWER_PLUGIN( EDA_3D_CANVAS* aViewport );
|
||||
|
||||
virtual ~NL_3D_VIEWER_PLUGIN();
|
||||
|
||||
/**
|
||||
* Set the connection to the 3Dconnexion driver to the focus state so that
|
||||
* 3DMouse data is routed here.
|
||||
*
|
||||
* @param aFocus is true to set the connection active.
|
||||
*/
|
||||
void SetFocus( bool aFocus = true );
|
||||
|
||||
private:
|
||||
NL_3D_VIEWER_PLUGIN_IMPL* m_impl;
|
||||
};
|
||||
|
||||
#endif // NL_3D_VIEWER_PLUGIN_H_
|
||||
@@ -1,659 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021 3Dconnexion
|
||||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nl_3d_viewer_plugin_impl.h"
|
||||
|
||||
// 3d-viewer
|
||||
#include <3d-viewer/3d_rendering/track_ball.h>
|
||||
#include <3d-viewer/3d_canvas/eda_3d_canvas.h>
|
||||
|
||||
// KiCAD includes
|
||||
#include <tool/action_manager.h>
|
||||
#include <tool/tool_manager.h>
|
||||
|
||||
// stdlib
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/mstream.h>
|
||||
|
||||
/**
|
||||
* Flag to enable the NL_3D_VIEWER_PLUGIN debug tracing.
|
||||
*
|
||||
* Use "KI_TRACE_NL_3D_VIEWER_PLUGIN" to enable.
|
||||
*
|
||||
* @ingroup trace_env_vars
|
||||
*/
|
||||
const wxChar* NL_3D_VIEWER_PLUGIN_IMPL::m_logTrace = wxT( "KI_TRACE_NL_3D_VIEWER_PLUGIN" );
|
||||
|
||||
|
||||
/**
|
||||
* Template to compare two glm::mat<T> values for equality within a required epsilon.
|
||||
*
|
||||
* @param aFirst value to compare.
|
||||
* @param aSecond value to compare.
|
||||
* @param aEpsilon allowed error.
|
||||
* @return true if the values considered equal within the specified epsilon, otherwise false.
|
||||
*/
|
||||
template <glm::length_t L, glm::length_t C, class T, glm::qualifier Q>
|
||||
bool equals( glm::mat<L, C, T, Q> const& aFirst, glm::mat<L, C, T, Q> const& aSecond,
|
||||
T aEpsilon = static_cast<T>( FLT_EPSILON * 10 ) )
|
||||
{
|
||||
T const* first = glm::value_ptr( aFirst );
|
||||
T const* second = glm::value_ptr( aSecond );
|
||||
|
||||
for( glm::length_t j = 0; j < L * C; ++j )
|
||||
{
|
||||
if( !equals( first[j], second[j], aEpsilon ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
NL_3D_VIEWER_PLUGIN_IMPL::NL_3D_VIEWER_PLUGIN_IMPL( EDA_3D_CANVAS* aCanvas ) :
|
||||
NAV_3D( false, false ), m_canvas( aCanvas ), m_capIsMoving( false )
|
||||
{
|
||||
m_camera = dynamic_cast<TRACK_BALL*>( m_canvas->GetCamera() );
|
||||
|
||||
PutProfileHint( "KiCAD 3D" );
|
||||
|
||||
EnableNavigation( true );
|
||||
PutFrameTimingSource( TimingSource::SpaceMouse );
|
||||
|
||||
exportCommandsAndImages();
|
||||
}
|
||||
|
||||
|
||||
NL_3D_VIEWER_PLUGIN_IMPL::~NL_3D_VIEWER_PLUGIN_IMPL()
|
||||
{
|
||||
EnableNavigation( false );
|
||||
}
|
||||
|
||||
|
||||
void NL_3D_VIEWER_PLUGIN_IMPL::SetFocus( bool aFocus )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "NL_3D_VIEWER_PLUGIN_IMPL::SetFocus %d" ), aFocus );
|
||||
NAV_3D::Write( navlib::focus_k, aFocus );
|
||||
}
|
||||
|
||||
// temporary store for the categories
|
||||
typedef std::map<std::string, TDx::CCommandTreeNode*> CATEGORY_STORE;
|
||||
|
||||
/**
|
||||
* Add a category to the store.
|
||||
*
|
||||
* The function adds category paths of the format "A.B" where B is a sub-category of A.
|
||||
*
|
||||
* @param aCategoryPath is the std::string representation of the category.
|
||||
* @param aCategoryStore is the CATEGORY_STORE instance to add to.
|
||||
* @return a CATEGORY_STORE::iterator where the category was added.
|
||||
*/
|
||||
CATEGORY_STORE::iterator add_category( std::string aCategoryPath, CATEGORY_STORE& aCategoryStore )
|
||||
{
|
||||
using TDx::SpaceMouse::CCategory;
|
||||
|
||||
CATEGORY_STORE::iterator parent_iter = aCategoryStore.begin();
|
||||
std::string::size_type pos = aCategoryPath.find_last_of( '.' );
|
||||
|
||||
if( pos != std::string::npos )
|
||||
{
|
||||
std::string parentPath = aCategoryPath.substr( 0, pos );
|
||||
parent_iter = aCategoryStore.find( parentPath );
|
||||
|
||||
if( parent_iter == aCategoryStore.end() )
|
||||
{
|
||||
parent_iter = add_category( parentPath, aCategoryStore );
|
||||
}
|
||||
}
|
||||
|
||||
std::string name = aCategoryPath.substr( pos + 1 );
|
||||
std::unique_ptr<CCategory> categoryNode =
|
||||
std::make_unique<CCategory>( aCategoryPath.c_str(), name.c_str() );
|
||||
|
||||
CATEGORY_STORE::iterator iter = aCategoryStore.insert(
|
||||
aCategoryStore.end(), CATEGORY_STORE::value_type( aCategoryPath, categoryNode.get() ) );
|
||||
|
||||
parent_iter->second->push_back( std::move( categoryNode ) );
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
void NL_3D_VIEWER_PLUGIN_IMPL::exportCommandsAndImages()
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "NL_3D_VIEWER_PLUGIN_IMPL::exportCommandsAndImages" ) );
|
||||
|
||||
std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
|
||||
|
||||
if( actions.size() == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using TDx::SpaceMouse::CCommand;
|
||||
using TDx::SpaceMouse::CCommandSet;
|
||||
|
||||
// The root action set node
|
||||
CCommandSet commandSet( "EDA_3D_CANVAS", "3D Viewer" );
|
||||
|
||||
// Activate the command set
|
||||
NAV_3D::PutActiveCommands( commandSet.GetId() );
|
||||
|
||||
// temporary store for the categories
|
||||
CATEGORY_STORE categoryStore;
|
||||
|
||||
std::vector<TDx::CImage> vImages;
|
||||
|
||||
// add the action set to the category_store
|
||||
categoryStore.insert( categoryStore.end(), CATEGORY_STORE::value_type( ".", &commandSet ) );
|
||||
|
||||
std::list<TOOL_ACTION*>::const_iterator it;
|
||||
|
||||
for( it = actions.begin(); it != actions.end(); ++it )
|
||||
{
|
||||
const TOOL_ACTION* action = *it;
|
||||
std::string label = action->GetLabel().ToStdString();
|
||||
|
||||
if( label.empty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name = action->GetName();
|
||||
|
||||
// Do no export commands for the pcbnew app.
|
||||
if( name.rfind( "pcbnew.", 0 ) == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string strCategory = action->GetToolName();
|
||||
CATEGORY_STORE::iterator iter = categoryStore.find( strCategory );
|
||||
|
||||
if( iter == categoryStore.end() )
|
||||
{
|
||||
iter = add_category( std::move( strCategory ), categoryStore );
|
||||
}
|
||||
|
||||
std::string description = action->GetDescription().ToStdString();
|
||||
|
||||
// Arbitrary 8-bit data stream
|
||||
wxMemoryOutputStream imageStream;
|
||||
|
||||
if( action->GetIcon() != BITMAPS::INVALID_BITMAP )
|
||||
{
|
||||
wxImage image = KiBitmap( action->GetIcon() ).ConvertToImage();
|
||||
image.SaveFile( imageStream, wxBitmapType::wxBITMAP_TYPE_PNG );
|
||||
image.Destroy();
|
||||
|
||||
if( imageStream.GetSize() )
|
||||
{
|
||||
wxStreamBuffer* streamBuffer = imageStream.GetOutputStreamBuffer();
|
||||
TDx::CImage tdxImage = TDx::CImage::FromData( "", 0, name.c_str() );
|
||||
tdxImage.AssignImage( std::string( reinterpret_cast<const char*>(
|
||||
streamBuffer->GetBufferStart() ),
|
||||
streamBuffer->GetBufferSize() ),
|
||||
0 );
|
||||
|
||||
wxLogTrace( m_logTrace, wxT( "Adding image for : %s" ), name );
|
||||
vImages.push_back( std::move( tdxImage ) );
|
||||
}
|
||||
}
|
||||
|
||||
wxLogTrace( m_logTrace, wxT( "Inserting command: %s, description: %s, in category: %s" ),
|
||||
name, description, iter->first );
|
||||
|
||||
iter->second->push_back(
|
||||
CCommand( std::move( name ), std::move( label ), std::move( description ) ) );
|
||||
}
|
||||
|
||||
NAV_3D::AddCommandSet( commandSet );
|
||||
NAV_3D::AddImages( vImages );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetCameraMatrix( navlib::matrix_t& matrix ) const
|
||||
{
|
||||
// cache the camera matrix so that we can tell if the view has been moved and
|
||||
// calculate a delta transform if required.
|
||||
m_cameraMatrix = m_camera->GetViewMatrix();
|
||||
|
||||
std::copy_n( glm::value_ptr( glm::inverse( m_cameraMatrix ) ), 16, matrix.m );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetPointerPosition( navlib::point_t& position ) const
|
||||
{
|
||||
SFVEC3F origin, direction;
|
||||
m_camera->MakeRayAtCurrentMousePosition( origin, direction );
|
||||
|
||||
position = { origin.x, origin.y, origin.z };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetViewExtents( navlib::box_t& extents ) const
|
||||
{
|
||||
if( m_camera->GetProjection() == PROJECTION_TYPE::PERSPECTIVE )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
|
||||
}
|
||||
|
||||
const CAMERA_FRUSTUM& f = m_camera->GetFrustum();
|
||||
|
||||
double half_width = f.fw / 2.;
|
||||
double half_height = f.fh / 2.;
|
||||
extents = { -half_width, -half_height, f.nearD, half_width, half_height, f.farD };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetViewFOV( double& aFov ) const
|
||||
{
|
||||
const CAMERA_FRUSTUM& f = m_camera->GetFrustum();
|
||||
|
||||
aFov = glm::radians( f.angle );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetViewFrustum( navlib::frustum_t& aFrustum ) const
|
||||
{
|
||||
if( m_camera->GetProjection() != PROJECTION_TYPE::PERSPECTIVE )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
|
||||
}
|
||||
|
||||
const CAMERA_FRUSTUM& f = m_camera->GetFrustum();
|
||||
double half_width = f.nw / 2.;
|
||||
double half_height = f.nh / 2.;
|
||||
aFrustum = { -half_width, half_width, -half_height, half_height, f.nearD, f.farD };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetIsViewPerspective( navlib::bool_t& perspective ) const
|
||||
{
|
||||
perspective = m_camera->GetProjection() == PROJECTION_TYPE::PERSPECTIVE ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetCameraMatrix( const navlib::matrix_t& aCameraMatrix )
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
glm::mat4 cam, viewMatrix;
|
||||
std::copy_n( aCameraMatrix.m, 16, glm::value_ptr( cam ) );
|
||||
viewMatrix = glm::inverse( cam );
|
||||
|
||||
glm::mat4 camera = m_camera->GetViewMatrix();
|
||||
|
||||
// The navlib does not move the camera in its z-axis in an orthographic projection
|
||||
// as this does not change the viewed object size. However ...
|
||||
|
||||
if( m_camera->GetProjection() == PROJECTION_TYPE::ORTHO )
|
||||
{
|
||||
// ... the CAMERA class couples zoom and distance to the object: we need to
|
||||
// ensure that The CAMERA's z position relative to the lookat_pos is not changed
|
||||
// in an orthographic projection.
|
||||
glm::vec4 lookat( m_camera->GetLookAtPos(), 1.0f );
|
||||
glm::vec4 lookat_new = viewMatrix * lookat;
|
||||
glm::vec4 lookat_old = camera * lookat;
|
||||
|
||||
viewMatrix[3].z += lookat_old.z - lookat_new.z;
|
||||
}
|
||||
|
||||
if( !equals( camera, m_cameraMatrix ) )
|
||||
{
|
||||
// Some other input device has moved the camera. Apply only the intended delta
|
||||
// transform ...
|
||||
m_camera->SetViewMatrix( viewMatrix * glm::inverse( m_cameraMatrix ) * camera );
|
||||
m_camera->Update();
|
||||
|
||||
// .., cache the intended camera matrix so that we can calculate the delta
|
||||
// transform when needed ...
|
||||
m_cameraMatrix = viewMatrix;
|
||||
|
||||
// ... and let the 3DMouse controller know, that something is amiss.
|
||||
return navlib::make_result_code( navlib::navlib_errc::error );
|
||||
}
|
||||
|
||||
m_camera->SetViewMatrix( viewMatrix );
|
||||
m_camera->Update();
|
||||
|
||||
// cache the view matrix so that we know when it has changed.
|
||||
m_cameraMatrix = m_camera->GetViewMatrix();
|
||||
|
||||
// The camera has a constraint on the z position so we need to check that ...
|
||||
|
||||
if( !equals( m_cameraMatrix[3].z, viewMatrix[3].z ) )
|
||||
{
|
||||
// ... and let the 3DMouse controller know, when something is amiss.
|
||||
return navlib::make_result_code( navlib::navlib_errc::error );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetViewExtents( const navlib::box_t& extents )
|
||||
{
|
||||
const CAMERA_FRUSTUM& f = m_camera->GetFrustum();
|
||||
|
||||
float factor = f.nw / ( extents.max_x - extents.min_x );
|
||||
float zoom = m_camera->GetZoom() / factor;
|
||||
|
||||
m_camera->Zoom( factor );
|
||||
|
||||
// The camera auto positions the camera to match the zoom values. We need to
|
||||
// update our cached camera matrix to match the new z value
|
||||
m_cameraMatrix[3].z = m_camera->GetViewMatrix()[3].z;
|
||||
|
||||
// The camera has a constraint on the zoom factor so we need to check that ...
|
||||
if( zoom != m_camera->GetZoom() )
|
||||
{
|
||||
// ... and let the 3DMouse controller know, when something is amiss.
|
||||
return navlib::make_result_code( navlib::navlib_errc::error );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetViewFOV( double fov )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::function_not_supported );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetViewFrustum( const navlib::frustum_t& frustum )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::permission_denied );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetModelExtents( navlib::box_t& extents ) const
|
||||
{
|
||||
SFVEC3F min = m_canvas->GetBoardAdapter().GetBBox().Min();
|
||||
SFVEC3F max = m_canvas->GetBoardAdapter().GetBBox().Max();
|
||||
|
||||
extents = { min.x, min.y, min.z, max.x, max.y, max.z };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetSelectionExtents( navlib::box_t& extents ) const
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetSelectionTransform( navlib::matrix_t& transform ) const
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetIsSelectionEmpty( navlib::bool_t& empty ) const
|
||||
{
|
||||
empty = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetSelectionTransform( const navlib::matrix_t& matrix )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetPivotPosition( navlib::point_t& position ) const
|
||||
{
|
||||
SFVEC3F lap = m_camera->GetLookAtPos();
|
||||
|
||||
position = { lap.x, lap.y, lap.z };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::IsUserPivot( navlib::bool_t& userPivot ) const
|
||||
{
|
||||
userPivot = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetPivotPosition( const navlib::point_t& position )
|
||||
{
|
||||
SFVEC3F pivotPos = SFVEC3F( position.x, position.y, position.z );
|
||||
|
||||
// Set the 3dmouse pivot position.
|
||||
m_canvas->Set3dmousePivotPos( pivotPos );
|
||||
|
||||
// Set the camera lookat pos.
|
||||
m_camera->SetLookAtPos_T1( pivotPos );
|
||||
|
||||
m_canvas->Request_refresh();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetPivotVisible( navlib::bool_t& visible ) const
|
||||
{
|
||||
visible = m_canvas->GetRender3dmousePivot();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetPivotVisible( bool visible )
|
||||
{
|
||||
m_canvas->SetRender3dmousePivot( visible );
|
||||
|
||||
m_canvas->Request_refresh();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetHitLookAt( navlib::point_t& position ) const
|
||||
{
|
||||
RAY mouseRay;
|
||||
mouseRay.Init( m_rayOrigin, m_rayDirection );
|
||||
|
||||
float hit;
|
||||
glm::vec3 vec;
|
||||
|
||||
// Test it with the board bounding box
|
||||
|
||||
if( m_canvas->GetBoardAdapter().GetBBox().Intersect( mouseRay, &hit ) )
|
||||
{
|
||||
vec = mouseRay.at( hit );
|
||||
position = { vec.x, vec.y, vec.z };
|
||||
return 0;
|
||||
}
|
||||
|
||||
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetHitAperture( double aperture )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::function_not_supported );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetHitDirection( const navlib::vector_t& direction )
|
||||
{
|
||||
m_rayDirection = { direction.x, direction.y, direction.z };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetHitLookFrom( const navlib::point_t& eye )
|
||||
{
|
||||
m_rayOrigin = { eye.x, eye.y, eye.z };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetHitSelectionOnly( bool onlySelection )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::function_not_supported );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetActiveCommand( std::string commandId )
|
||||
{
|
||||
if( commandId.empty() )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
|
||||
TOOL_ACTION* context = nullptr;
|
||||
|
||||
for( std::list<TOOL_ACTION*>::const_iterator it = actions.begin(); it != actions.end(); it++ )
|
||||
{
|
||||
TOOL_ACTION* action = *it;
|
||||
std::string nm = action->GetName();
|
||||
|
||||
if( commandId == nm )
|
||||
{
|
||||
context = action;
|
||||
}
|
||||
}
|
||||
|
||||
if( context != nullptr )
|
||||
{
|
||||
wxWindow* parent = m_canvas->GetParent();
|
||||
|
||||
// Only allow command execution if the window is enabled. i.e. there is not a modal dialog
|
||||
// currently active.
|
||||
|
||||
if( parent->IsEnabled() )
|
||||
{
|
||||
TOOL_MANAGER* tool_manager = static_cast<PCB_BASE_FRAME*>( parent )->GetToolManager();
|
||||
|
||||
if( tool_manager == nullptr )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
|
||||
}
|
||||
|
||||
// Get the selection to use to test if the action is enabled
|
||||
SELECTION& sel = tool_manager->GetToolHolder()->GetCurrentSelection();
|
||||
|
||||
bool runAction = true;
|
||||
|
||||
if( const ACTION_CONDITIONS* aCond =
|
||||
tool_manager->GetActionManager()->GetCondition( *context ) )
|
||||
{
|
||||
runAction = aCond->enableCondition( sel );
|
||||
}
|
||||
|
||||
if( runAction )
|
||||
{
|
||||
tool_manager->RunAction( *context, true );
|
||||
m_canvas->Request_refresh();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetSettingsChanged( long change )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetMotionFlag( bool value )
|
||||
{
|
||||
m_capIsMoving = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetTransaction( long value )
|
||||
{
|
||||
if( value != 0L )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
m_canvas->Request_refresh( true );
|
||||
wxLogTrace( m_logTrace, wxT( "End of transaction" ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::SetCameraTarget( const navlib::point_t& position )
|
||||
{
|
||||
return navlib::make_result_code( navlib::navlib_errc::function_not_supported );
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetFrontView( navlib::matrix_t& matrix ) const
|
||||
{
|
||||
matrix = { 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1 };
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetCoordinateSystem( navlib::matrix_t& matrix ) const
|
||||
{
|
||||
// Use the right-handed coordinate system X-right, Z-up, Y-in (row vectors)
|
||||
matrix = { 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1 };
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long NL_3D_VIEWER_PLUGIN_IMPL::GetIsViewRotatable( navlib::bool_t& isRotatable ) const
|
||||
{
|
||||
isRotatable = true;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021 3Dconnexion
|
||||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file nl_3d_viewer_plugin_impl.h
|
||||
* @brief Declaration of the NL_3D_VIEWER_PLUGIN_IMPL class
|
||||
*/
|
||||
|
||||
#ifndef NL_3D_VIEWER_PLUGIN_IMPL_H_
|
||||
#define NL_3D_VIEWER_PLUGIN_IMPL_H_
|
||||
|
||||
// TDxWare SDK.
|
||||
#include <SpaceMouse/CNavigation3D.hpp>
|
||||
|
||||
// KiCAD
|
||||
#include <include/plugins/3dapi/xv3d_types.h>
|
||||
|
||||
// wx
|
||||
#include <wx/chartype.h>
|
||||
|
||||
// glm
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// Forward declarations.
|
||||
class EDA_3D_CANVAS;
|
||||
class TRACK_BALL;
|
||||
|
||||
// Convenience typedef.
|
||||
typedef TDx::SpaceMouse::Navigation3D::CNavigation3D NAV_3D;
|
||||
|
||||
/**
|
||||
* The class that implements the accessors and mutators required for
|
||||
* 3D navigation in an EDA_3D_CANVAS using a SpaceMouse.
|
||||
*/
|
||||
class NL_3D_VIEWER_PLUGIN_IMPL : public NAV_3D
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes a new instance of the NL_3DVIEWER_PLUGIN.
|
||||
*
|
||||
* @param aCanvas is the viewport to be navigated.
|
||||
*/
|
||||
NL_3D_VIEWER_PLUGIN_IMPL( EDA_3D_CANVAS* aCanvas );
|
||||
|
||||
virtual ~NL_3D_VIEWER_PLUGIN_IMPL();
|
||||
|
||||
/**
|
||||
* Set the connection to the 3Dconnexion driver to the focus state so that
|
||||
* 3DMouse data is routed here.
|
||||
*
|
||||
* @param aFocus is true to set the connection active.
|
||||
*/
|
||||
void SetFocus( bool aFocus = true );
|
||||
|
||||
private:
|
||||
/**
|
||||
* Export the invocable actions and images to the 3Dconnexion UI.
|
||||
*/
|
||||
void exportCommandsAndImages();
|
||||
|
||||
long GetCameraMatrix( navlib::matrix_t& aMatrix ) const override;
|
||||
long GetPointerPosition( navlib::point_t& aPosition ) const override;
|
||||
long GetViewExtents( navlib::box_t& aExtents ) const override;
|
||||
long GetViewFOV( double& aFov ) const override;
|
||||
long GetViewFrustum( navlib::frustum_t& aFrustum ) const override;
|
||||
long GetIsViewPerspective( navlib::bool_t& aPerspective ) const override;
|
||||
long SetCameraMatrix( const navlib::matrix_t& aMatrix ) override;
|
||||
long SetViewExtents( const navlib::box_t& aExtents ) override;
|
||||
long SetViewFOV( double aFov ) override;
|
||||
long SetViewFrustum( const navlib::frustum_t& aFrustum ) override;
|
||||
long GetModelExtents( navlib::box_t& aExtents ) const override;
|
||||
long GetSelectionExtents( navlib::box_t& aExtents ) const override;
|
||||
long GetSelectionTransform( navlib::matrix_t& aTransform ) const override;
|
||||
long GetIsSelectionEmpty( navlib::bool_t& aEmpty ) const override;
|
||||
long SetSelectionTransform( const navlib::matrix_t& aMatrix ) override;
|
||||
long GetPivotPosition( navlib::point_t& aPosition ) const override;
|
||||
long IsUserPivot( navlib::bool_t& aUserPivot ) const override;
|
||||
long SetPivotPosition( const navlib::point_t& aPosition ) override;
|
||||
long GetPivotVisible( navlib::bool_t& aVisible ) const override;
|
||||
long SetPivotVisible( bool aVisible ) override;
|
||||
long GetHitLookAt( navlib::point_t& aPosition ) const override;
|
||||
long SetHitAperture( double aAperture ) override;
|
||||
long SetHitDirection( const navlib::vector_t& aDirection ) override;
|
||||
long SetHitLookFrom( const navlib::point_t& aPosition ) override;
|
||||
long SetHitSelectionOnly( bool aSelectionOnly ) override;
|
||||
long SetActiveCommand( std::string aCommandId ) override;
|
||||
|
||||
long SetSettingsChanged( long aChangeNumber ) override;
|
||||
long SetMotionFlag( bool aValue ) override;
|
||||
long SetTransaction( long aValue ) override;
|
||||
long SetCameraTarget( const navlib::point_t& aPosition ) override;
|
||||
|
||||
long GetFrontView( navlib::matrix_t& aMatrix ) const override;
|
||||
long GetCoordinateSystem( navlib::matrix_t& aMatrix ) const override;
|
||||
long GetIsViewRotatable( navlib::bool_t& isRotatable ) const override;
|
||||
|
||||
private:
|
||||
EDA_3D_CANVAS* m_canvas;
|
||||
TRACK_BALL* m_camera;
|
||||
bool m_capIsMoving;
|
||||
double m_newWidth;
|
||||
SFVEC3F m_rayOrigin;
|
||||
SFVEC3F m_rayDirection;
|
||||
|
||||
// The cached CAMERA affine matrix. This is used to determine if the camera has been
|
||||
// moved. The camera matrix is the world to camera affine.
|
||||
mutable glm::mat4 m_cameraMatrix;
|
||||
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable the trace output of this class.
|
||||
* The debug output can be turned on by setting the WXTRACE environment variable to
|
||||
* "KI_TRACE_NL_3DVIEWER_PLUGIN". See the wxWidgets documentation on wxLogTrace for
|
||||
* more information.
|
||||
*/
|
||||
static const wxChar* m_logTrace;
|
||||
};
|
||||
|
||||
#endif // NL_3D_VIEWER_PLUGIN_IMPL_H_
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 Jean-Pierre Charras, jp.charras@wanadoo.fr
|
||||
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
|
||||
* Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file 3d_read_mesh.cpp
|
||||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <common.h>
|
||||
#include <macros.h>
|
||||
#include <kicad_string.h>
|
||||
#include <appl_wxstruct.h>
|
||||
|
||||
#include <3d_viewer.h>
|
||||
#include <info3d_visu.h>
|
||||
#include "3d_struct.h"
|
||||
#include "modelparsers.h"
|
||||
|
||||
// Imported function:
|
||||
extern void Set_Object_Data( std::vector< S3D_VERTEX >& aVertices, double aBiuTo3DUnits );
|
||||
|
||||
|
||||
S3D_MODEL_PARSER* S3D_MODEL_PARSER::Create( S3D_MASTER* aMaster,
|
||||
const wxString aExtension )
|
||||
{
|
||||
if ( aExtension == wxT( "x3d" ) )
|
||||
{
|
||||
return new X3D_MODEL_PARSER( aMaster );
|
||||
}
|
||||
else if ( aExtension == wxT( "wrl" ) )
|
||||
{
|
||||
return new VRML_MODEL_PARSER( aMaster );
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int S3D_MASTER::ReadData()
|
||||
{
|
||||
if( m_Shape3DName.IsEmpty() )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Expand any environment variables embedded in footprint's m_Shape3DName field.
|
||||
wxString filename = wxExpandEnvVars( m_Shape3DName );
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
filename.Replace( wxT( "/" ), wxT( "\\" ) );
|
||||
#else
|
||||
filename.Replace( wxT( "\\" ), wxT( "/" ) );
|
||||
#endif
|
||||
|
||||
if( !wxFileName::FileExists( filename ) )
|
||||
{
|
||||
wxLogDebug( wxT( "3D shape '%s' not found, even tried '%s' after env var substitution." ),
|
||||
GetChars( m_Shape3DName ),
|
||||
GetChars( filename )
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wxFileName fn( filename );
|
||||
|
||||
wxString extension = fn.GetExt();
|
||||
S3D_MODEL_PARSER* parser = S3D_MODEL_PARSER::Create( this, extension );
|
||||
|
||||
if( parser )
|
||||
{
|
||||
parser->Load( filename );
|
||||
delete parser;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogDebug( wxT( "Unknown file type '%s'" ), GetChars( extension ) );
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int STRUCT_3D_SHAPE::ReadData( FILE* file, int* LineNum )
|
||||
{
|
||||
char line[512];
|
||||
|
||||
while( GetLine( file, line, LineNum, 512 ) )
|
||||
{
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2016-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file buffers_debug.cpp
|
||||
* @brief
|
||||
*/
|
||||
|
||||
#include "buffers_debug.h"
|
||||
#include <wx/image.h> // Used for save an image to disk
|
||||
|
||||
/**
|
||||
* @param aRGBpixelBuffer from the wxWidgets documentation
|
||||
* "The data given must have the size (width*height*3).
|
||||
* The data must have been allocated with malloc(), NOT with operator new."
|
||||
*/
|
||||
static void dbg_save_rgb_buffer( const wxString& aFileName, unsigned char *aRGBpixelBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize )
|
||||
{
|
||||
wxImage image( aXSize, aYSize );
|
||||
image.SetData( aRGBpixelBuffer );
|
||||
image = image.Mirror( false );
|
||||
image.SaveFile( aFileName + wxT( ".png" ), wxBITMAP_TYPE_PNG );
|
||||
image.Destroy();
|
||||
}
|
||||
|
||||
|
||||
void DBG_SaveBuffer( const wxString& aFileName, const unsigned char *aInBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize )
|
||||
{
|
||||
const unsigned int wxh = aXSize * aYSize;
|
||||
|
||||
unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 );
|
||||
|
||||
for( unsigned int i = 0; i < wxh; ++i )
|
||||
{
|
||||
unsigned char v = aInBuffer[i];
|
||||
|
||||
// Set RGB value with all same values intensities
|
||||
pixelbuffer[i * 3 + 0] = v;
|
||||
pixelbuffer[i * 3 + 1] = v;
|
||||
pixelbuffer[i * 3 + 2] = v;
|
||||
}
|
||||
|
||||
dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize );
|
||||
}
|
||||
|
||||
|
||||
void DBG_SaveBuffer( const wxString& aFileName, const float *aInBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize )
|
||||
{
|
||||
const unsigned int wxh = aXSize * aYSize;
|
||||
|
||||
unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 );
|
||||
|
||||
for( unsigned int i = 0; i < wxh; ++i )
|
||||
{
|
||||
const unsigned char v = (unsigned char)glm::min( (int)(aInBuffer[i] * 255.0f), 255 );
|
||||
|
||||
// Set RGB value with all same values intensities
|
||||
pixelbuffer[i * 3 + 0] = v;
|
||||
pixelbuffer[i * 3 + 1] = v;
|
||||
pixelbuffer[i * 3 + 2] = v;
|
||||
}
|
||||
|
||||
dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize );
|
||||
}
|
||||
|
||||
|
||||
void DBG_SaveBuffer( const wxString& aFileName, const SFVEC3F *aInBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize )
|
||||
{
|
||||
const unsigned int wxh = aXSize * aYSize;
|
||||
|
||||
unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 );
|
||||
|
||||
for( unsigned int i = 0; i < wxh; ++i )
|
||||
{
|
||||
const SFVEC3F &v = aInBuffer[i];
|
||||
const unsigned int ix3 = i * 3;
|
||||
|
||||
// Set RGB value with all same values intensities
|
||||
pixelbuffer[ix3 + 0] = (unsigned char) glm::min( (int) ( v.r * 255.0f ), 255 );
|
||||
pixelbuffer[ix3 + 1] = (unsigned char) glm::min( (int) ( v.g * 255.0f ), 255 );
|
||||
pixelbuffer[ix3 + 2] = (unsigned char) glm::min( (int) ( v.b * 255.0f ), 255 );
|
||||
}
|
||||
|
||||
dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize );
|
||||
}
|
||||
|
||||
|
||||
void DBG_SaveNormalsBuffer( const wxString& aFileName, const SFVEC3F *aInNormalsBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize )
|
||||
{
|
||||
const unsigned int wxh = aXSize * aYSize;
|
||||
|
||||
unsigned char *pixelbuffer = (unsigned char*) malloc( wxh * 3 );
|
||||
|
||||
for( unsigned int i = 0; i < wxh; ++i )
|
||||
{
|
||||
const SFVEC3F &v = aInNormalsBuffer[i];
|
||||
const unsigned int ix3 = i * 3;
|
||||
|
||||
// Set RGB value with all same values intensities
|
||||
pixelbuffer[ix3 + 0] = (unsigned char) glm::min( (int) ( ( v.r + 1.0f ) * 127.0f ), 255 );
|
||||
pixelbuffer[ix3 + 1] = (unsigned char) glm::min( (int) ( ( v.g + 1.0f ) * 127.0f ), 255 );
|
||||
pixelbuffer[ix3 + 2] = (unsigned char) glm::min( (int) ( ( v.b + 1.0f ) * 127.0f ), 255 );
|
||||
}
|
||||
|
||||
dbg_save_rgb_buffer( aFileName, pixelbuffer, aXSize, aYSize );
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2016-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file buffers_debug.h
|
||||
* @brief
|
||||
*/
|
||||
|
||||
#ifndef BUFFER_DEBUG_H
|
||||
#define BUFFER_DEBUG_H
|
||||
|
||||
#include <plugins/3dapi/xv3d_types.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
void DBG_SaveBuffer( const wxString& aFileName, const unsigned char *aInBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize );
|
||||
|
||||
void DBG_SaveBuffer( const wxString& aFileName, const float *aInBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize );
|
||||
|
||||
void DBG_SaveBuffer( const wxString& aFileName, const SFVEC3F *aInBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize );
|
||||
|
||||
void DBG_SaveNormalsBuffer( const wxString& aFileName, const SFVEC3F *aInNormalsBuffer,
|
||||
unsigned int aXSize, unsigned int aYSize );
|
||||
|
||||
#endif // BUFFER_DEBUG_H
|
||||
@@ -1,669 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file camera.cpp
|
||||
*/
|
||||
|
||||
#include "camera.h"
|
||||
#include <wx/log.h>
|
||||
#include <algorithm>
|
||||
|
||||
// A helper function to normalize aAngle between -2PI and +2PI
|
||||
inline void normalise2PI( float& aAngle )
|
||||
{
|
||||
while( aAngle > 0.0 )
|
||||
aAngle -= static_cast<float>( M_PI * 2.0f );
|
||||
|
||||
while( aAngle < 0.0 )
|
||||
aAngle += static_cast<float>( M_PI * 2.0f );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup trace_env_vars
|
||||
*/
|
||||
const wxChar *CAMERA::m_logTrace = wxT( "KI_TRACE_CAMERA" );
|
||||
|
||||
const float CAMERA::DEFAULT_MIN_ZOOM = 0.020f;
|
||||
const float CAMERA::DEFAULT_MAX_ZOOM = 2.0f;
|
||||
|
||||
|
||||
CAMERA::CAMERA( float aInitialDistance )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "CAMERA::CAMERA" ) );
|
||||
|
||||
m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -aInitialDistance );
|
||||
m_board_lookat_pos_init = SFVEC3F( 0.0f );
|
||||
m_windowSize = SFVEC2I( 0, 0 );
|
||||
m_projectionType = PROJECTION_TYPE::PERSPECTIVE;
|
||||
m_interpolation_mode = CAMERA_INTERPOLATION::BEZIER;
|
||||
|
||||
m_minZoom = DEFAULT_MIN_ZOOM;
|
||||
m_maxZoom = DEFAULT_MAX_ZOOM;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::Reset()
|
||||
{
|
||||
m_parametersChanged = true;
|
||||
m_projectionMatrix = glm::mat4( 1.0f );
|
||||
m_projectionMatrixInv = glm::mat4( 1.0f );
|
||||
m_rotationMatrix = glm::mat4( 1.0f );
|
||||
m_rotationMatrixAux = glm::mat4( 1.0f );
|
||||
m_lastPosition = wxPoint( 0, 0 );
|
||||
|
||||
m_zoom = 1.0f;
|
||||
m_zoom_t0 = 1.0f;
|
||||
m_zoom_t1 = 1.0f;
|
||||
m_camera_pos = m_camera_pos_init;
|
||||
m_camera_pos_t0 = m_camera_pos_init;
|
||||
m_camera_pos_t1 = m_camera_pos_init;
|
||||
m_lookat_pos = m_board_lookat_pos_init;
|
||||
m_lookat_pos_t0 = m_board_lookat_pos_init;
|
||||
m_lookat_pos_t1 = m_board_lookat_pos_init;
|
||||
|
||||
m_rotate_aux = SFVEC3F( 0.0f );
|
||||
m_rotate_aux_t0 = SFVEC3F( 0.0f );
|
||||
m_rotate_aux_t1 = SFVEC3F( 0.0f );
|
||||
|
||||
updateRotationMatrix();
|
||||
updateViewMatrix();
|
||||
m_viewMatrixInverse = glm::inverse( m_viewMatrix );
|
||||
m_scr_nX.clear();
|
||||
m_scr_nY.clear();
|
||||
rebuildProjection();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::Reset_T1()
|
||||
{
|
||||
m_camera_pos_t1 = m_camera_pos_init;
|
||||
m_zoom_t1 = 1.0f;
|
||||
m_rotate_aux_t1 = SFVEC3F( 0.0f );
|
||||
m_lookat_pos_t1 = m_board_lookat_pos_init;
|
||||
|
||||
// Since 0 = 2pi, we want to reset the angle to be the closest
|
||||
// one to where we currently are. That ensures that we rotate
|
||||
// the board around the smallest distance getting there.
|
||||
if( m_rotate_aux_t0.x > M_PI )
|
||||
m_rotate_aux_t1.x = static_cast<float>( 2.0f * M_PI );
|
||||
|
||||
if( m_rotate_aux_t0.y > M_PI )
|
||||
m_rotate_aux_t1.y = static_cast<float>( 2.0f * M_PI );
|
||||
|
||||
if( m_rotate_aux_t0.z > M_PI )
|
||||
m_rotate_aux_t1.z = static_cast<float>( 2.0f * M_PI );
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::SetBoardLookAtPos( const SFVEC3F& aBoardPos )
|
||||
{
|
||||
if( m_board_lookat_pos_init != aBoardPos )
|
||||
{
|
||||
m_board_lookat_pos_init = aBoardPos;
|
||||
m_lookat_pos = aBoardPos;
|
||||
|
||||
m_parametersChanged = true;
|
||||
|
||||
updateViewMatrix();
|
||||
updateFrustum();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::zoomChanged()
|
||||
{
|
||||
if( m_zoom < m_minZoom )
|
||||
m_zoom = m_minZoom;
|
||||
|
||||
if( m_zoom > m_maxZoom )
|
||||
m_zoom = m_maxZoom;
|
||||
|
||||
m_camera_pos.z = m_camera_pos_init.z * m_zoom;
|
||||
|
||||
updateViewMatrix();
|
||||
rebuildProjection();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::updateViewMatrix()
|
||||
{
|
||||
m_viewMatrix = glm::translate( glm::mat4( 1.0f ), m_camera_pos ) *
|
||||
m_rotationMatrix * m_rotationMatrixAux *
|
||||
glm::translate( glm::mat4( 1.0f ), -m_lookat_pos );
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::updateRotationMatrix()
|
||||
{
|
||||
m_rotationMatrixAux = glm::rotate( glm::mat4( 1.0f ), m_rotate_aux.x,
|
||||
SFVEC3F( 1.0f, 0.0f, 0.0f ) );
|
||||
normalise2PI( m_rotate_aux.x );
|
||||
|
||||
m_rotationMatrixAux = glm::rotate( m_rotationMatrixAux, m_rotate_aux.y,
|
||||
SFVEC3F( 0.0f, 1.0f, 0.0f ) );
|
||||
normalise2PI( m_rotate_aux.y );
|
||||
|
||||
m_rotationMatrixAux = glm::rotate( m_rotationMatrixAux, m_rotate_aux.z,
|
||||
SFVEC3F( 0.0f, 0.0f, 1.0f ) );
|
||||
normalise2PI( m_rotate_aux.z );
|
||||
|
||||
m_parametersChanged = true;
|
||||
|
||||
updateViewMatrix();
|
||||
updateFrustum();
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 CAMERA::GetRotationMatrix() const
|
||||
{
|
||||
return m_rotationMatrix * m_rotationMatrixAux;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::SetRotationMatrix( const glm::mat4& aRotation )
|
||||
{
|
||||
m_parametersChanged = true;
|
||||
std::copy_n( glm::value_ptr( aRotation * glm::inverse( m_rotationMatrixAux ) ), 12,
|
||||
glm::value_ptr( m_rotationMatrix ) );
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::rebuildProjection()
|
||||
{
|
||||
if( ( m_windowSize.x == 0 ) || ( m_windowSize.y == 0 ) )
|
||||
return;
|
||||
|
||||
m_frustum.ratio = (float) m_windowSize.x / (float)m_windowSize.y;
|
||||
m_frustum.farD = glm::length( m_camera_pos_init ) * m_maxZoom * 2.0f;
|
||||
|
||||
switch( m_projectionType )
|
||||
{
|
||||
default:
|
||||
case PROJECTION_TYPE::PERSPECTIVE:
|
||||
|
||||
m_frustum.nearD = 0.10f;
|
||||
|
||||
m_frustum.angle = 45.0f;
|
||||
|
||||
m_projectionMatrix = glm::perspective( glm::radians( m_frustum.angle ), m_frustum.ratio,
|
||||
m_frustum.nearD, m_frustum.farD );
|
||||
|
||||
m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
|
||||
|
||||
m_frustum.tang = glm::tan( glm::radians( m_frustum.angle ) * 0.5f );
|
||||
|
||||
m_focalLen.x = ( (float)m_windowSize.y / (float)m_windowSize.x ) / m_frustum.tang;
|
||||
m_focalLen.y = 1.0f / m_frustum.tang;
|
||||
|
||||
m_frustum.nh = 2.0f * m_frustum.nearD * m_frustum.tang;
|
||||
m_frustum.nw = m_frustum.nh * m_frustum.ratio;
|
||||
m_frustum.fh = 2.0f * m_frustum.farD * m_frustum.tang;
|
||||
m_frustum.fw = m_frustum.fh * m_frustum.ratio;
|
||||
break;
|
||||
|
||||
case PROJECTION_TYPE::ORTHO:
|
||||
|
||||
// Keep the viewed plane at (m_camera_pos_init * m_zoom) the same dimensions in both projections.
|
||||
m_frustum.angle = 45.0f;
|
||||
m_frustum.tang = glm::tan( glm::radians( m_frustum.angle ) * 0.5f );
|
||||
|
||||
m_frustum.nearD = -m_frustum.farD; // Use a symmetrical clip plane for ortho projection
|
||||
|
||||
const float orthoReductionFactor =
|
||||
glm::length( m_camera_pos_init ) * m_zoom * m_frustum.tang;
|
||||
|
||||
// Initialize Projection Matrix for Orthographic View
|
||||
m_projectionMatrix = glm::ortho( -m_frustum.ratio * orthoReductionFactor,
|
||||
m_frustum.ratio * orthoReductionFactor,
|
||||
-orthoReductionFactor,
|
||||
orthoReductionFactor,
|
||||
m_frustum.nearD, m_frustum.farD );
|
||||
|
||||
m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
|
||||
|
||||
m_frustum.nw = orthoReductionFactor * 2.0f * m_frustum.ratio;
|
||||
m_frustum.nh = orthoReductionFactor * 2.0f;
|
||||
m_frustum.fw = m_frustum.nw;
|
||||
m_frustum.fh = m_frustum.nh;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if( ( m_windowSize.x > 0 ) && ( m_windowSize.y > 0 ) )
|
||||
{
|
||||
m_scr_nX.resize( m_windowSize.x + 1 );
|
||||
m_scr_nY.resize( m_windowSize.y + 1 );
|
||||
|
||||
// Precalc X values for camera -> ray generation
|
||||
for( unsigned int x = 0; x < (unsigned int)m_windowSize.x + 1; ++x )
|
||||
{
|
||||
// Converts 0.0 .. 1.0
|
||||
const float xNormalizedDeviceCoordinates = ( ( (float)x + 0.5f ) /
|
||||
(m_windowSize.x - 0.0f) );
|
||||
|
||||
// Converts -1.0 .. 1.0
|
||||
m_scr_nX[x] = 2.0f * xNormalizedDeviceCoordinates - 1.0f;
|
||||
}
|
||||
|
||||
// Precalc Y values for camera -> ray generation
|
||||
for( unsigned int y = 0; y < (unsigned int)m_windowSize.y + 1 ; ++y )
|
||||
{
|
||||
// Converts 0.0 .. 1.0
|
||||
const float yNormalizedDeviceCoordinates = ( ( (float)y + 0.5f ) /
|
||||
(m_windowSize.y - 0.0f) );
|
||||
|
||||
// Converts -1.0 .. 1.0
|
||||
m_scr_nY[y] = 2.0f * yNormalizedDeviceCoordinates - 1.0f;
|
||||
}
|
||||
|
||||
updateFrustum();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::updateFrustum()
|
||||
{
|
||||
// Update matrix and vectors
|
||||
m_viewMatrixInverse = glm::inverse( m_viewMatrix );
|
||||
|
||||
m_right = glm::normalize( SFVEC3F( m_viewMatrixInverse *
|
||||
glm::vec4( SFVEC3F( 1.0, 0.0, 0.0 ), 0.0 ) ) );
|
||||
|
||||
m_up = glm::normalize( SFVEC3F( m_viewMatrixInverse *
|
||||
glm::vec4( SFVEC3F( 0.0, 1.0, 0.0 ), 0.0 ) ) );
|
||||
|
||||
m_dir = glm::normalize( SFVEC3F( m_viewMatrixInverse *
|
||||
glm::vec4( SFVEC3F( 0.0, 0.0, 1.0 ), 0.0 ) ) );
|
||||
|
||||
m_pos = SFVEC3F( m_viewMatrixInverse * glm::vec4( SFVEC3F( 0.0, 0.0, 0.0 ), 1.0 ) );
|
||||
|
||||
/*
|
||||
* Frustum is a implementation based on a tutorial by
|
||||
* http://www.lighthouse3d.com/tutorials/view-frustum-culling/
|
||||
*/
|
||||
|
||||
const SFVEC3F half_right_nw = m_right * m_frustum.nw * 0.5f;
|
||||
const SFVEC3F half_right_fw = m_right * m_frustum.fw * 0.5f;
|
||||
const SFVEC3F half_up_nh = m_up * m_frustum.nh * 0.5f;
|
||||
const SFVEC3F half_up_fh = m_up * m_frustum.fh * 0.5f;
|
||||
|
||||
// compute the centers of the near and far planes
|
||||
m_frustum.nc = m_pos - m_dir * m_frustum.nearD;
|
||||
m_frustum.fc = m_pos - m_dir * m_frustum.farD;
|
||||
|
||||
// compute the 4 corners of the frustum on the near plane
|
||||
m_frustum.ntl = m_frustum.nc + half_up_nh - half_right_nw;
|
||||
m_frustum.ntr = m_frustum.nc + half_up_nh + half_right_nw;
|
||||
m_frustum.nbl = m_frustum.nc - half_up_nh - half_right_nw;
|
||||
m_frustum.nbr = m_frustum.nc - half_up_nh + half_right_nw;
|
||||
|
||||
// compute the 4 corners of the frustum on the far plane
|
||||
m_frustum.ftl = m_frustum.fc + half_up_fh - half_right_fw;
|
||||
m_frustum.ftr = m_frustum.fc + half_up_fh + half_right_fw;
|
||||
m_frustum.fbl = m_frustum.fc - half_up_fh - half_right_fw;
|
||||
m_frustum.fbr = m_frustum.fc - half_up_fh + half_right_fw;
|
||||
|
||||
if( ( m_windowSize.x > 0 ) && ( m_windowSize.y > 0 ) )
|
||||
{
|
||||
// Reserve size for precalc values
|
||||
m_right_nX.resize( m_windowSize.x + 1 );
|
||||
m_up_nY.resize( m_windowSize.y + 1 );
|
||||
|
||||
// Precalc X values for camera -> ray generation
|
||||
for( unsigned int x = 0; x < ( (unsigned int) m_windowSize.x + 1 ); ++x )
|
||||
m_right_nX[x] = half_right_nw * m_scr_nX[x];
|
||||
|
||||
// Precalc Y values for camera -> ray generation
|
||||
for( unsigned int y = 0; y < ( (unsigned int) m_windowSize.y + 1 ); ++y )
|
||||
m_up_nY[y] = half_up_nh * m_scr_nY[y];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::MakeRay( const SFVEC2I& aWindowPos, SFVEC3F& aOutOrigin,
|
||||
SFVEC3F& aOutDirection ) const
|
||||
{
|
||||
wxASSERT( aWindowPos.x < m_windowSize.x );
|
||||
wxASSERT( aWindowPos.y < m_windowSize.y );
|
||||
|
||||
aOutOrigin = m_frustum.nc + m_up_nY[aWindowPos.y] + m_right_nX[aWindowPos.x];
|
||||
|
||||
switch( m_projectionType )
|
||||
{
|
||||
default:
|
||||
case PROJECTION_TYPE::PERSPECTIVE:
|
||||
aOutDirection = glm::normalize( aOutOrigin - m_pos );
|
||||
break;
|
||||
|
||||
case PROJECTION_TYPE::ORTHO:
|
||||
aOutDirection = -m_dir + SFVEC3F( FLT_EPSILON );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::MakeRay( const SFVEC2F& aWindowPos, SFVEC3F& aOutOrigin,
|
||||
SFVEC3F& aOutDirection ) const
|
||||
{
|
||||
wxASSERT( aWindowPos.x < (float)m_windowSize.x );
|
||||
wxASSERT( aWindowPos.y < (float)m_windowSize.y );
|
||||
|
||||
const SFVEC2F floorWinPos_f = glm::floor( aWindowPos );
|
||||
const SFVEC2I floorWinPos_i = (SFVEC2I)floorWinPos_f;
|
||||
const SFVEC2F relativeWinPos = aWindowPos - floorWinPos_f;
|
||||
|
||||
// Note: size of vectors m_up and m_right are m_windowSize + 1
|
||||
const SFVEC3F up_plus_right = m_up_nY[floorWinPos_i.y] * (1.0f - relativeWinPos.y) +
|
||||
m_up_nY[floorWinPos_i.y + 1] * relativeWinPos.y +
|
||||
m_right_nX[floorWinPos_i.x] * (1.0f - relativeWinPos.x) +
|
||||
m_right_nX[floorWinPos_i.x + 1] * relativeWinPos.x;
|
||||
|
||||
aOutOrigin = up_plus_right + m_frustum.nc;
|
||||
|
||||
switch( m_projectionType )
|
||||
{
|
||||
default:
|
||||
case PROJECTION_TYPE::PERSPECTIVE:
|
||||
aOutDirection = glm::normalize( aOutOrigin - m_pos );
|
||||
break;
|
||||
|
||||
case PROJECTION_TYPE::ORTHO:
|
||||
aOutDirection = -m_dir + SFVEC3F( FLT_EPSILON );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::MakeRayAtCurrentMousePosition( SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const
|
||||
{
|
||||
const SFVEC2I windowPos = SFVEC2I( m_lastPosition.x, m_windowSize.y - m_lastPosition.y );
|
||||
|
||||
if( ( 0 < windowPos.x ) && ( windowPos.x < m_windowSize.x ) &&
|
||||
( 0 < windowPos.y ) && ( windowPos.y < m_windowSize.y ) )
|
||||
{
|
||||
MakeRay( windowPos, aOutOrigin, aOutDirection );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const glm::mat4& CAMERA::GetProjectionMatrix() const
|
||||
{
|
||||
return m_projectionMatrix;
|
||||
}
|
||||
|
||||
|
||||
const glm::mat4& CAMERA::GetProjectionMatrixInv() const
|
||||
{
|
||||
return m_projectionMatrixInv;
|
||||
}
|
||||
|
||||
|
||||
float CAMERA::GetCameraMinDimension() const
|
||||
{
|
||||
return -m_camera_pos_init.z * m_frustum.tang;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::ResetXYpos()
|
||||
{
|
||||
m_parametersChanged = true;
|
||||
m_camera_pos.x = 0.0f;
|
||||
m_camera_pos.y = 0.0f;
|
||||
|
||||
updateViewMatrix();
|
||||
updateFrustum();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::ResetXYpos_T1()
|
||||
{
|
||||
m_camera_pos_t1.x = 0.0f;
|
||||
m_camera_pos_t1.y = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
const glm::mat4& CAMERA::GetViewMatrix() const
|
||||
{
|
||||
return m_viewMatrix;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::SetViewMatrix( glm::mat4 aViewMatrix )
|
||||
{
|
||||
SetRotationMatrix( aViewMatrix );
|
||||
|
||||
// The look at position in the view frame.
|
||||
glm::vec4 lookat = aViewMatrix * glm::vec4( m_lookat_pos, 1.0f );
|
||||
|
||||
wxLogTrace( m_logTrace,
|
||||
wxT( "CAMERA::SetViewMatrix aViewMatrix[3].z =%f, old_zoom=%f, new_zoom=%f, "
|
||||
"m[3].z=%f" ),
|
||||
aViewMatrix[3].z, m_zoom, lookat.z / m_camera_pos_init.z, lookat.z );
|
||||
|
||||
m_zoom = lookat.z / m_camera_pos_init.z;
|
||||
|
||||
if( m_zoom > m_maxZoom )
|
||||
{
|
||||
m_zoom = m_maxZoom;
|
||||
aViewMatrix[3].z += -lookat.z + m_maxZoom * m_camera_pos_init.z;
|
||||
}
|
||||
else if( m_zoom < m_minZoom )
|
||||
{
|
||||
m_zoom = m_minZoom;
|
||||
aViewMatrix[3].z += -lookat.z + m_minZoom * m_camera_pos_init.z;
|
||||
}
|
||||
|
||||
m_viewMatrix = std::move( aViewMatrix );
|
||||
m_camera_pos = m_viewMatrix
|
||||
* glm::inverse( m_rotationMatrix * m_rotationMatrixAux
|
||||
* glm::translate( glm::mat4( 1.0f ), -m_lookat_pos ) )[3];
|
||||
}
|
||||
|
||||
|
||||
const glm::mat4& CAMERA::GetViewMatrix_Inv() const
|
||||
{
|
||||
return m_viewMatrixInverse;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::SetCurMousePosition( const wxPoint& aNewMousePosition )
|
||||
{
|
||||
m_lastPosition = aNewMousePosition;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::ToggleProjection()
|
||||
{
|
||||
if( m_projectionType == PROJECTION_TYPE::ORTHO )
|
||||
m_projectionType = PROJECTION_TYPE::PERSPECTIVE;
|
||||
else
|
||||
m_projectionType = PROJECTION_TYPE::ORTHO;
|
||||
|
||||
rebuildProjection();
|
||||
}
|
||||
|
||||
|
||||
bool CAMERA::SetCurWindowSize( const wxSize& aSize )
|
||||
{
|
||||
const SFVEC2I newSize = SFVEC2I( aSize.x, aSize.y );
|
||||
|
||||
if( m_windowSize != newSize )
|
||||
{
|
||||
m_windowSize = newSize;
|
||||
rebuildProjection();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::ZoomReset()
|
||||
{
|
||||
m_zoom = 1.0f;
|
||||
|
||||
m_camera_pos.z = m_camera_pos_init.z;
|
||||
|
||||
updateViewMatrix();
|
||||
rebuildProjection();
|
||||
}
|
||||
|
||||
|
||||
bool CAMERA::Zoom( float aFactor )
|
||||
{
|
||||
if( ( m_zoom <= m_minZoom && aFactor > 1 ) || ( m_zoom >= m_maxZoom && aFactor < 1 )
|
||||
|| aFactor == 1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float zoom = m_zoom;
|
||||
m_zoom /= aFactor;
|
||||
|
||||
if( m_zoom <= m_minZoom && aFactor > 1 )
|
||||
{
|
||||
aFactor = zoom / m_minZoom;
|
||||
m_zoom = m_minZoom;
|
||||
}
|
||||
else if( m_zoom >= m_maxZoom && aFactor < 1 )
|
||||
{
|
||||
aFactor = zoom / m_maxZoom;
|
||||
m_zoom = m_maxZoom;
|
||||
}
|
||||
|
||||
m_camera_pos.z /= aFactor;
|
||||
|
||||
updateViewMatrix();
|
||||
rebuildProjection();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CAMERA::Zoom_T1( float aFactor )
|
||||
{
|
||||
if( ( m_zoom <= m_minZoom && aFactor > 1 ) || ( m_zoom >= m_maxZoom && aFactor < 1 )
|
||||
|| aFactor == 1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_zoom_t1 = m_zoom / aFactor;
|
||||
|
||||
if( m_zoom_t1 < m_minZoom )
|
||||
m_zoom_t1 = m_minZoom;
|
||||
|
||||
if( m_zoom_t1 > m_maxZoom )
|
||||
m_zoom_t1 = m_maxZoom;
|
||||
|
||||
m_camera_pos_t1.z = m_camera_pos_init.z * m_zoom_t1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::RotateX( float aAngleInRadians )
|
||||
{
|
||||
m_rotate_aux.x += aAngleInRadians;
|
||||
updateRotationMatrix();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::RotateY( float aAngleInRadians )
|
||||
{
|
||||
m_rotate_aux.y += aAngleInRadians;
|
||||
updateRotationMatrix();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::RotateZ( float aAngleInRadians )
|
||||
{
|
||||
m_rotate_aux.z += aAngleInRadians;
|
||||
updateRotationMatrix();
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::RotateX_T1( float aAngleInRadians )
|
||||
{
|
||||
m_rotate_aux_t1.x += aAngleInRadians;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::RotateY_T1( float aAngleInRadians )
|
||||
{
|
||||
m_rotate_aux_t1.y += aAngleInRadians;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::RotateZ_T1( float aAngleInRadians )
|
||||
{
|
||||
m_rotate_aux_t1.z += aAngleInRadians;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::SetT0_and_T1_current_T()
|
||||
{
|
||||
m_camera_pos_t0 = m_camera_pos;
|
||||
m_lookat_pos_t0 = m_lookat_pos;
|
||||
m_rotate_aux_t0 = m_rotate_aux;
|
||||
m_zoom_t0 = m_zoom;
|
||||
|
||||
m_camera_pos_t1 = m_camera_pos;
|
||||
m_lookat_pos_t1 = m_lookat_pos;
|
||||
m_rotate_aux_t1 = m_rotate_aux;
|
||||
m_zoom_t1 = m_zoom;
|
||||
}
|
||||
|
||||
|
||||
void CAMERA::Interpolate( float t )
|
||||
{
|
||||
wxASSERT( t >= 0.0f );
|
||||
|
||||
const float t0 = 1.0f - t;
|
||||
|
||||
m_camera_pos = m_camera_pos_t0 * t0 + m_camera_pos_t1 * t;
|
||||
m_lookat_pos = m_lookat_pos_t0 * t0 + m_lookat_pos_t1 * t;
|
||||
m_rotate_aux = m_rotate_aux_t0 * t0 + m_rotate_aux_t1 * t;
|
||||
m_zoom = m_zoom_t0 * t0 + m_zoom_t1 * t;
|
||||
|
||||
m_parametersChanged = true;
|
||||
|
||||
updateRotationMatrix();
|
||||
rebuildProjection();
|
||||
}
|
||||
|
||||
|
||||
bool CAMERA::ParametersChanged()
|
||||
{
|
||||
const bool parametersChanged = m_parametersChanged;
|
||||
|
||||
m_parametersChanged = false;
|
||||
|
||||
return parametersChanged;
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file camera.h
|
||||
* @brief Define an abstract camera.
|
||||
*/
|
||||
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include "../3d_rendering/raytracing/ray.h"
|
||||
#include <wx/gdicmn.h> // for wxSize
|
||||
#include <vector>
|
||||
|
||||
enum class PROJECTION_TYPE
|
||||
{
|
||||
ORTHO = 0,
|
||||
PERSPECTIVE
|
||||
};
|
||||
|
||||
/**
|
||||
* Frustum is a implementation based on a tutorial by
|
||||
* http://www.lighthouse3d.com/tutorials/view-frustum-culling/
|
||||
*/
|
||||
struct CAMERA_FRUSTUM
|
||||
{
|
||||
SFVEC3F nc;
|
||||
SFVEC3F fc;
|
||||
SFVEC3F ntl; ///< Near Top Left
|
||||
SFVEC3F ntr; ///< Near Top Right
|
||||
SFVEC3F nbl; ///< Near Bottom Left
|
||||
SFVEC3F nbr; ///< Near Bottom Right
|
||||
SFVEC3F ftl; ///< Far Top Left
|
||||
SFVEC3F ftr; ///< Far Top Right
|
||||
SFVEC3F fbl; ///< Far Bottom Left
|
||||
SFVEC3F fbr; ///< Far Bottom Right
|
||||
float nearD, farD, ratio, angle, tang;
|
||||
float nw, nh, fw, fh;
|
||||
};
|
||||
|
||||
|
||||
enum class CAMERA_INTERPOLATION
|
||||
{
|
||||
LINEAR,
|
||||
EASING_IN_OUT, // Quadratic
|
||||
BEZIER,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A class used to derive camera objects from.
|
||||
*
|
||||
* It must be derived by other classes to implement a real camera object.
|
||||
*/
|
||||
class CAMERA
|
||||
{
|
||||
public:
|
||||
static const float DEFAULT_MIN_ZOOM;
|
||||
static const float DEFAULT_MAX_ZOOM;
|
||||
|
||||
/**
|
||||
* Initialize a camera.
|
||||
*
|
||||
* @param aInitialDistance Initial Z-distance to the board
|
||||
*/
|
||||
explicit CAMERA( float aInitialDistance );
|
||||
|
||||
virtual ~CAMERA()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rotation matrix to be applied in a transformation camera.
|
||||
*
|
||||
* @return the rotation matrix of the camera
|
||||
*/
|
||||
glm::mat4 GetRotationMatrix() const;
|
||||
|
||||
const glm::mat4& GetViewMatrix() const;
|
||||
const glm::mat4& GetViewMatrix_Inv() const;
|
||||
|
||||
const glm::mat4& GetProjectionMatrix() const;
|
||||
const glm::mat4& GetProjectionMatrixInv() const;
|
||||
|
||||
const SFVEC3F& GetRight() const { return m_right; }
|
||||
const SFVEC3F& GetUp() const { return m_up; }
|
||||
const SFVEC3F& GetDir() const { return m_dir; }
|
||||
const SFVEC3F& GetPos() const { return m_pos; }
|
||||
const SFVEC2F& GetFocalLen() const { return m_focalLen; }
|
||||
float GetNear() const { return m_frustum.nearD; }
|
||||
float GetFar() const { return m_frustum.farD; }
|
||||
const CAMERA_FRUSTUM& GetFrustum() const { return m_frustum; }
|
||||
const SFVEC3F& GetLookAtPos() const { return m_lookat_pos; }
|
||||
|
||||
/**
|
||||
* Set the rotation matrix to be applied in a transformation camera, without
|
||||
* making any new calculations on camera.
|
||||
*
|
||||
* @param aRotation is the total rotation matrix of the camera.
|
||||
*/
|
||||
void SetRotationMatrix( const glm::mat4& aRotation );
|
||||
|
||||
/**
|
||||
* Set the affine matrix to be applied to a transformation camera.
|
||||
*
|
||||
* @param aViewMatrix is the affine matrix of the camera. The affine matrix
|
||||
* maps coordinates in the world frame to those in the
|
||||
* camera frame.
|
||||
*/
|
||||
void SetViewMatrix( glm::mat4 aViewMatrix );
|
||||
|
||||
void SetBoardLookAtPos( const SFVEC3F& aBoardPos );
|
||||
|
||||
void SetLookAtPos_T1( const SFVEC3F& aLookAtPos )
|
||||
{
|
||||
m_lookat_pos_t1 = aLookAtPos;
|
||||
}
|
||||
|
||||
const SFVEC3F& GetLookAtPos_T1() const { return m_lookat_pos_t1; }
|
||||
|
||||
const SFVEC3F& GetCameraPos() const { return m_camera_pos; }
|
||||
const SFVEC3F& GetCameraInitPos() const { return m_camera_pos_init; }
|
||||
|
||||
float GetCameraMinDimension() const;
|
||||
|
||||
/**
|
||||
* Calculate a new mouse drag position
|
||||
*/
|
||||
virtual void Drag( const wxPoint& aNewMousePosition ) = 0;
|
||||
|
||||
virtual void Pan( const wxPoint& aNewMousePosition ) = 0;
|
||||
|
||||
virtual void Pan( const SFVEC3F& aDeltaOffsetInc ) = 0;
|
||||
|
||||
virtual void Pan_T1( const SFVEC3F& aDeltaOffsetInc ) = 0;
|
||||
|
||||
/**
|
||||
* Reset the camera to initial state
|
||||
*/
|
||||
virtual void Reset();
|
||||
virtual void Reset_T1();
|
||||
|
||||
void ResetXYpos();
|
||||
void ResetXYpos_T1();
|
||||
|
||||
/**
|
||||
* Get the current mouse position.
|
||||
*/
|
||||
const wxPoint& GetCurMousePosition() { return m_lastPosition; }
|
||||
|
||||
/**
|
||||
* Update the current mouse position without make any new calculations on camera.
|
||||
*/
|
||||
void SetCurMousePosition( const wxPoint& aPosition );
|
||||
|
||||
void ToggleProjection();
|
||||
PROJECTION_TYPE GetProjection() { return m_projectionType; }
|
||||
void SetProjection( PROJECTION_TYPE aProjection ) { m_projectionType = aProjection; }
|
||||
|
||||
/**
|
||||
* Update the windows size of the camera.
|
||||
*
|
||||
* @return true if the windows size changed since last time.
|
||||
*/
|
||||
bool SetCurWindowSize( const wxSize& aSize );
|
||||
|
||||
void ZoomReset();
|
||||
|
||||
bool Zoom( float aFactor );
|
||||
|
||||
bool Zoom_T1( float aFactor );
|
||||
|
||||
float GetZoom() const { return m_zoom; }
|
||||
|
||||
float GetMinZoom() { return m_minZoom; }
|
||||
void SetMinZoom( float minZoom )
|
||||
{
|
||||
m_minZoom = minZoom;
|
||||
zoomChanged();
|
||||
}
|
||||
|
||||
float GetMaxZoom() { return m_maxZoom; }
|
||||
void SetMaxZoom( float maxZoom )
|
||||
{
|
||||
m_maxZoom = maxZoom;
|
||||
zoomChanged();
|
||||
}
|
||||
|
||||
void RotateX( float aAngleInRadians );
|
||||
void RotateY( float aAngleInRadians );
|
||||
void RotateZ( float aAngleInRadians );
|
||||
|
||||
void RotateX_T1( float aAngleInRadians );
|
||||
void RotateY_T1( float aAngleInRadians );
|
||||
void RotateZ_T1( float aAngleInRadians );
|
||||
|
||||
/**
|
||||
* This will set T0 and T1 with the current values.
|
||||
*/
|
||||
virtual void SetT0_and_T1_current_T();
|
||||
|
||||
/**
|
||||
* It will update the matrix to interpolate between T0 and T1 values.
|
||||
*
|
||||
* @param t the interpolation time, between 0.0f and 1.0f (it will clamp if >1).
|
||||
*/
|
||||
virtual void Interpolate( float t );
|
||||
|
||||
void SetInterpolateMode( CAMERA_INTERPOLATION aInterpolateMode )
|
||||
{
|
||||
m_interpolation_mode = aInterpolateMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if some of the parameters in camera was changed, it will reset the flag.
|
||||
*/
|
||||
bool ParametersChanged();
|
||||
|
||||
/**
|
||||
* @return true if some of the parameters in camera was changed, it will NOT reset the flag.
|
||||
*/
|
||||
bool ParametersChangedQuery() const { return m_parametersChanged; }
|
||||
|
||||
/**
|
||||
* Make a ray based on a windows screen position.
|
||||
*
|
||||
* @param aWindowPos the windows buffer position.
|
||||
* @param aOutOrigin out origin position of the ray.
|
||||
* @param aOutDirection out direction
|
||||
*/
|
||||
void MakeRay( const SFVEC2I& aWindowPos, SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const;
|
||||
|
||||
/**
|
||||
* Make a ray based on a windows screen position, it will interpolate based on the
|
||||
* \a aWindowPos.
|
||||
*
|
||||
* @param aWindowPos the windows buffer position (float value).
|
||||
* @param aOutOrigin out origin position of the ray.
|
||||
* @param aOutDirection out direction.
|
||||
*/
|
||||
void MakeRay( const SFVEC2F& aWindowPos, SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const;
|
||||
|
||||
/**
|
||||
* Make a ray based on the latest mouse position.
|
||||
*
|
||||
* @param aOutOrigin out origin position of the ray.
|
||||
* @param aOutDirection out direction.
|
||||
*/
|
||||
void MakeRayAtCurrentMousePosition( SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const;
|
||||
|
||||
/**
|
||||
* Update the camera.
|
||||
*/
|
||||
void Update() { updateFrustum(); }
|
||||
|
||||
protected:
|
||||
void zoomChanged();
|
||||
void rebuildProjection();
|
||||
void updateFrustum();
|
||||
void updateViewMatrix();
|
||||
|
||||
void updateRotationMatrix();
|
||||
|
||||
/**
|
||||
* 3D zoom value -- Z-distance is scaled by it
|
||||
*/
|
||||
float m_zoom;
|
||||
float m_zoom_t0;
|
||||
float m_zoom_t1;
|
||||
|
||||
/**
|
||||
* Possible 3D zoom range
|
||||
*/
|
||||
float m_minZoom;
|
||||
float m_maxZoom;
|
||||
|
||||
/**
|
||||
* The window size that this camera is working.
|
||||
*/
|
||||
SFVEC2I m_windowSize;
|
||||
|
||||
/**
|
||||
* The last mouse position in the screen
|
||||
*/
|
||||
wxPoint m_lastPosition;
|
||||
|
||||
glm::mat4 m_rotationMatrix;
|
||||
glm::mat4 m_rotationMatrixAux;
|
||||
glm::mat4 m_viewMatrix;
|
||||
glm::mat4 m_viewMatrixInverse;
|
||||
glm::mat4 m_projectionMatrix;
|
||||
glm::mat4 m_projectionMatrixInv;
|
||||
PROJECTION_TYPE m_projectionType;
|
||||
|
||||
CAMERA_FRUSTUM m_frustum;
|
||||
|
||||
SFVEC3F m_right;
|
||||
SFVEC3F m_up;
|
||||
SFVEC3F m_dir;
|
||||
SFVEC3F m_pos;
|
||||
|
||||
SFVEC2F m_focalLen;
|
||||
|
||||
SFVEC3F m_camera_pos_init;
|
||||
SFVEC3F m_camera_pos;
|
||||
SFVEC3F m_camera_pos_t0;
|
||||
SFVEC3F m_camera_pos_t1;
|
||||
|
||||
SFVEC3F m_lookat_pos;
|
||||
SFVEC3F m_lookat_pos_t0;
|
||||
SFVEC3F m_lookat_pos_t1;
|
||||
SFVEC3F m_board_lookat_pos_init; ///< Default boardlookat position (the board center).
|
||||
|
||||
SFVEC3F m_rotate_aux; ///< Stores the rotation angle auxiliary.
|
||||
SFVEC3F m_rotate_aux_t0;
|
||||
SFVEC3F m_rotate_aux_t1;
|
||||
|
||||
CAMERA_INTERPOLATION m_interpolation_mode;
|
||||
|
||||
/**
|
||||
* Precalc values array used to calc ray for each pixel (constant for the same window size).
|
||||
*/
|
||||
std::vector< float > m_scr_nX;
|
||||
std::vector< float > m_scr_nY;
|
||||
|
||||
/**
|
||||
* Precalc values array used to calc ray for each pixel, for X and Y axis of each new
|
||||
* camera position.
|
||||
*/
|
||||
std::vector< SFVEC3F > m_right_nX;
|
||||
std::vector< SFVEC3F > m_up_nY;
|
||||
|
||||
/**
|
||||
* Set to true if any of the parameters in the camera was changed
|
||||
*/
|
||||
bool m_parametersChanged;
|
||||
|
||||
/**
|
||||
* Trace mask used to enable or disable the trace output of this class.
|
||||
*
|
||||
* The debug output can be turned on by setting the WXTRACE environment variable to
|
||||
* "KI_TRACE_CAMERA". See the wxWidgets documentation on wxLogTrace for
|
||||
* more information.
|
||||
*/
|
||||
static const wxChar* m_logTrace;
|
||||
};
|
||||
|
||||
#endif // CAMERA_H
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file color_rgb.cpp
|
||||
*/
|
||||
|
||||
|
||||
#include "color_rgb.h"
|
||||
|
||||
|
||||
COLOR_RGB::COLOR_RGB( const SFVEC3F& aColor )
|
||||
{
|
||||
r = (unsigned int) glm::clamp( (int) ( aColor.r * 255 ), 0, 255 );
|
||||
g = (unsigned int) glm::clamp( (int) ( aColor.g * 255 ), 0, 255 );
|
||||
b = (unsigned int) glm::clamp( (int) ( aColor.b * 255 ), 0, 255 );
|
||||
}
|
||||
|
||||
|
||||
COLOR_RGB BlendColor( const COLOR_RGB& aC1, const COLOR_RGB& aC2 )
|
||||
{
|
||||
const unsigned int r = aC1.r + aC2.r;
|
||||
const unsigned int g = aC1.g + aC2.g;
|
||||
const unsigned int b = aC1.b + aC2.b;
|
||||
|
||||
return COLOR_RGB( ( r >> 1 ), ( g >> 1 ), ( b >> 1 ) );
|
||||
}
|
||||
|
||||
|
||||
COLOR_RGB BlendColor( const COLOR_RGB& aC1, const COLOR_RGB& aC2, const COLOR_RGB& aC3 )
|
||||
{
|
||||
const unsigned int r = aC1.r + aC2.r + aC3.r;
|
||||
const unsigned int g = aC1.g + aC2.g + aC3.g;
|
||||
const unsigned int b = aC1.b + aC2.b + aC3.b;
|
||||
|
||||
return COLOR_RGB( ( r / 3 ), ( g / 3 ), ( b / 3 ) );
|
||||
}
|
||||
|
||||
|
||||
COLOR_RGB BlendColor( const COLOR_RGB& aC1, const COLOR_RGB& aC2, const COLOR_RGB& aC3,
|
||||
const COLOR_RGB& aC4 )
|
||||
{
|
||||
const unsigned int r = aC1.r + aC2.r + aC3.r + aC4.r;
|
||||
const unsigned int g = aC1.g + aC2.g + aC3.g + aC4.g;
|
||||
const unsigned int b = aC1.b + aC2.b + aC3.b + aC4.b;
|
||||
|
||||
return COLOR_RGB( ( r >> 2 ), ( g >> 2 ), ( b >> 2 ) );
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file color_rgb.h
|
||||
*/
|
||||
|
||||
#ifndef COLOR_RGB_H
|
||||
#define COLOR_RGB_H
|
||||
|
||||
#include <plugins/3dapi/xv3d_types.h>
|
||||
|
||||
union COLOR_RGB
|
||||
{
|
||||
unsigned char c[3];
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned char r;
|
||||
unsigned char g;
|
||||
unsigned char b;
|
||||
};
|
||||
|
||||
COLOR_RGB( const SFVEC3F& aColor );
|
||||
COLOR_RGB() { r = 0; g = 0; b = 0; }
|
||||
COLOR_RGB( unsigned char aR, unsigned char aG, unsigned char aB )
|
||||
{
|
||||
r = aR;
|
||||
g = aG;
|
||||
b = aB;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
COLOR_RGB BlendColor( const COLOR_RGB& aC1, const COLOR_RGB& aC2 );
|
||||
COLOR_RGB BlendColor( const COLOR_RGB& aC1, const COLOR_RGB& aC2, const COLOR_RGB& aC3 );
|
||||
COLOR_RGB BlendColor( const COLOR_RGB& aC1, const COLOR_RGB& aC2, const COLOR_RGB& aC3,
|
||||
const COLOR_RGB& aC4 );
|
||||
|
||||
#endif // COLOR_RGB_H
|
||||
@@ -1,600 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file image.cpp
|
||||
* @brief one 8bit-channel image implementation.
|
||||
*/
|
||||
|
||||
#include "image.h"
|
||||
#include "buffers_debug.h"
|
||||
#include <cstring> // For memcpy
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
#ifndef CLAMP
|
||||
#define CLAMP( n, min, max ) {if( n < min ) n=min; else if( n > max ) n = max;}
|
||||
#endif
|
||||
|
||||
|
||||
IMAGE::IMAGE( unsigned int aXsize, unsigned int aYsize )
|
||||
{
|
||||
m_wxh = aXsize * aYsize;
|
||||
m_pixels = new unsigned char[m_wxh];
|
||||
memset( m_pixels, 0, m_wxh );
|
||||
m_width = aXsize;
|
||||
m_height = aYsize;
|
||||
m_wraping = IMAGE_WRAP::CLAMP;
|
||||
}
|
||||
|
||||
|
||||
IMAGE::IMAGE( const IMAGE& aSrcImage )
|
||||
{
|
||||
m_wxh = aSrcImage.GetWidth() * aSrcImage.GetHeight();
|
||||
m_pixels = new unsigned char[m_wxh];
|
||||
memcpy( m_pixels, aSrcImage.GetBuffer(), m_wxh );
|
||||
m_width = aSrcImage.GetWidth();
|
||||
m_height = aSrcImage.GetHeight();
|
||||
m_wraping = IMAGE_WRAP::CLAMP;
|
||||
}
|
||||
|
||||
|
||||
IMAGE::~IMAGE()
|
||||
{
|
||||
delete[] m_pixels;
|
||||
}
|
||||
|
||||
|
||||
unsigned char* IMAGE::GetBuffer() const
|
||||
{
|
||||
return m_pixels;
|
||||
}
|
||||
|
||||
|
||||
bool IMAGE::wrapCoords( int* aXo, int* aYo ) const
|
||||
{
|
||||
int x = *aXo;
|
||||
int y = *aYo;
|
||||
|
||||
switch( m_wraping )
|
||||
{
|
||||
case IMAGE_WRAP::CLAMP:
|
||||
x = ( x < 0 ) ? 0 : x;
|
||||
x = ( x >= (int) ( m_width - 1 ) ) ? ( m_width - 1 ) : x;
|
||||
y = ( y < 0 ) ? 0 : y;
|
||||
y = ( y >= (int) ( m_height - 1 ) ) ? ( m_height - 1 ) : y;
|
||||
break;
|
||||
|
||||
case IMAGE_WRAP::WRAP:
|
||||
x = ( x < 0 ) ? ( ( m_width - 1 ) + x ) : x;
|
||||
x = ( x >= (int) ( m_width - 1 ) ) ? ( x - m_width ) : x;
|
||||
y = ( y < 0 ) ? ( ( m_height - 1 ) + y ) : y;
|
||||
y = ( y >= (int) ( m_height - 1 ) ) ? ( y - m_height ) : y;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( ( x < 0 ) || ( x >= (int) m_width ) || ( y < 0 ) || ( y >= (int) m_height ) )
|
||||
return false;
|
||||
|
||||
*aXo = x;
|
||||
*aYo = y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::plot8CircleLines( int aCx, int aCy, int aX, int aY, unsigned char aValue )
|
||||
{
|
||||
Hline( aCx - aX, aCx + aX, aCy + aY, aValue );
|
||||
Hline( aCx - aX, aCx + aX, aCy - aY, aValue );
|
||||
Hline( aCx - aY, aCx + aY, aCy + aX, aValue );
|
||||
Hline( aCx - aY, aCx + aY, aCy - aX, aValue );
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::Setpixel( int aX, int aY, unsigned char aValue )
|
||||
{
|
||||
if( wrapCoords( &aX, &aY ) )
|
||||
m_pixels[aX + aY * m_width] = aValue;
|
||||
}
|
||||
|
||||
|
||||
unsigned char IMAGE::Getpixel( int aX, int aY ) const
|
||||
{
|
||||
if( wrapCoords( &aX, &aY ) )
|
||||
return m_pixels[aX + aY * m_width];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::Hline( int aXStart, int aXEnd, int aY, unsigned char aValue )
|
||||
{
|
||||
if( ( aY < 0 ) || ( aY >= (int) m_height ) || ( ( aXStart < 0 ) && ( aXEnd < 0 ) )
|
||||
|| ( ( aXStart >= (int) m_width ) && ( aXEnd >= (int) m_width ) ) )
|
||||
return;
|
||||
|
||||
if( aXStart > aXEnd )
|
||||
{
|
||||
int swap = aXStart;
|
||||
|
||||
aXStart = aXEnd;
|
||||
aXEnd = swap;
|
||||
}
|
||||
|
||||
// Clamp line
|
||||
if( aXStart < 0 )
|
||||
aXStart = 0;
|
||||
|
||||
if( aXEnd >= (int)m_width )
|
||||
aXEnd = m_width - 1;
|
||||
|
||||
unsigned char* pixelPtr = &m_pixels[aXStart + aY * m_width];
|
||||
unsigned char* pixelPtrEnd = pixelPtr + (unsigned int) ( ( aXEnd - aXStart ) + 1 );
|
||||
|
||||
while( pixelPtr < pixelPtrEnd )
|
||||
{
|
||||
*pixelPtr = aValue;
|
||||
pixelPtr++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Based on paper
|
||||
// http://web.engr.oregonstate.edu/~sllu/bcircle.pdf
|
||||
void IMAGE::CircleFilled( int aCx, int aCy, int aRadius, unsigned char aValue )
|
||||
{
|
||||
int x = aRadius;
|
||||
int y = 0;
|
||||
int xChange = 1 - 2 * aRadius;
|
||||
int yChange = 0;
|
||||
int radiusError = 0;
|
||||
|
||||
while( x >= y )
|
||||
{
|
||||
plot8CircleLines( aCx, aCy, x, y, aValue );
|
||||
y++;
|
||||
radiusError += yChange;
|
||||
yChange += 2;
|
||||
|
||||
if( ( 2 * radiusError + xChange ) > 0 )
|
||||
{
|
||||
x--;
|
||||
radiusError += xChange;
|
||||
xChange += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::Invert()
|
||||
{
|
||||
for( unsigned int it = 0; it < m_wxh; it++ )
|
||||
m_pixels[it] = 255 - m_pixels[it];
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::CopyFull( const IMAGE* aImgA, const IMAGE* aImgB, IMAGE_OP aOperation )
|
||||
{
|
||||
int aV, bV;
|
||||
|
||||
if( aOperation == IMAGE_OP::RAW )
|
||||
{
|
||||
if( aImgA == nullptr )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ( aImgA == nullptr ) || ( aImgB == nullptr ) )
|
||||
return;
|
||||
}
|
||||
|
||||
switch( aOperation )
|
||||
{
|
||||
case IMAGE_OP::RAW:
|
||||
memcpy( m_pixels, aImgA->m_pixels, m_wxh );
|
||||
break;
|
||||
|
||||
case IMAGE_OP::ADD:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
aV = (aV + bV);
|
||||
aV = (aV > 255)?255:aV;
|
||||
|
||||
m_pixels[it] = aV;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::SUB:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
aV = (aV - bV);
|
||||
aV = (aV < 0)?0:aV;
|
||||
|
||||
m_pixels[it] = aV;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::DIF:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
m_pixels[it] = abs( aV - bV );
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::MUL:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
m_pixels[it] =
|
||||
(unsigned char) ( ( ( (float) aV / 255.0f ) * ( (float) bV / 255.0f ) ) * 255 );
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::AND:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
m_pixels[it] = aImgA->m_pixels[it] & aImgB->m_pixels[it];
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::OR:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
m_pixels[it] = aImgA->m_pixels[it] | aImgB->m_pixels[it];
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::XOR:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
m_pixels[it] = aImgA->m_pixels[it] ^ aImgB->m_pixels[it];
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::BLEND50:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
m_pixels[it] = (aV + bV) / 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::MIN:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
m_pixels[it] = (aV < bV)?aV:bV;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_OP::MAX:
|
||||
for( unsigned int it = 0;it < m_wxh; it++ )
|
||||
{
|
||||
aV = aImgA->m_pixels[it];
|
||||
bV = aImgB->m_pixels[it];
|
||||
|
||||
m_pixels[it] = (aV > bV)?aV:bV;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TIP: If you want create or test filters you can use GIMP
|
||||
// with a generic convolution matrix and get the values from there.
|
||||
// http://docs.gimp.org/nl/plug-in-convmatrix.html
|
||||
// clang-format off
|
||||
static const S_FILTER FILTERS[] = {
|
||||
// IMAGE_FILTER::HIPASS
|
||||
{
|
||||
{ { 0, -1, -1, -1, 0},
|
||||
{-1, 2, -4, 2, -1},
|
||||
{-1, -4, 13, -4, -1},
|
||||
{-1, 2, -4, 2, -1},
|
||||
{ 0, -1, -1, -1, 0}
|
||||
},
|
||||
7,
|
||||
255
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::GAUSSIAN_BLUR
|
||||
{
|
||||
{ { 3, 5, 7, 5, 3},
|
||||
{ 5, 9, 12, 9, 5},
|
||||
{ 7, 12, 20, 12, 7},
|
||||
{ 5, 9, 12, 9, 5},
|
||||
{ 3, 5, 7, 5, 3}
|
||||
},
|
||||
182,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::GAUSSIAN_BLUR2
|
||||
{
|
||||
{ { 1, 4, 7, 4, 1},
|
||||
{ 4, 16, 26, 16, 4},
|
||||
{ 7, 26, 41, 26, 7},
|
||||
{ 4, 16, 26, 16, 4},
|
||||
{ 1, 4, 7, 4, 1}
|
||||
},
|
||||
273,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::INVERT_BLUR
|
||||
{
|
||||
{ { 0, 0, 0, 0, 0},
|
||||
{ 0, 0, -1, 0, 0},
|
||||
{ 0, -1, 0, -1, 0},
|
||||
{ 0, 0, -1, 0, 0},
|
||||
{ 0, 0, 0, 0, 0}
|
||||
},
|
||||
4,
|
||||
255
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::CARTOON
|
||||
{
|
||||
{ {-1, -1, -1, -1, 0},
|
||||
{-1, 0, 0, 0, 0},
|
||||
{-1, 0, 4, 0, 0},
|
||||
{ 0, 0, 0, 1, 0},
|
||||
{ 0, 0, 0, 0, 4}
|
||||
},
|
||||
3,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::EMBOSS
|
||||
{
|
||||
{ {-1, -1, -1, -1, 0},
|
||||
{-1, -1, -1, 0, 1},
|
||||
{-1, -1, 0, 1, 1},
|
||||
{-1, 0, 1, 1, 1},
|
||||
{ 0, 1, 1, 1, 1}
|
||||
},
|
||||
1,
|
||||
128
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::SHARPEN
|
||||
{
|
||||
{ {-1, -1, -1, -1, -1},
|
||||
{-1, 2, 2, 2, -1},
|
||||
{-1, 2, 8, 2, -1},
|
||||
{-1, 2, 2, 2, -1},
|
||||
{-1, -1, -1, -1, -1}
|
||||
},
|
||||
8,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::MELT
|
||||
{
|
||||
{ { 4, 2, 6, 8, 1},
|
||||
{ 1, 2, 5, 4, 2},
|
||||
{ 0, -1, 1, -1, 0},
|
||||
{ 0, 0, -2, 0, 0},
|
||||
{ 0, 0, 0, 0, 0}
|
||||
},
|
||||
32,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::SOBEL_GX
|
||||
{
|
||||
{ { 0, 0, 0, 0, 0},
|
||||
{ 0, -1, 0, 1, 0},
|
||||
{ 0, -2, 0, 2, 0},
|
||||
{ 0, -1, 0, 1, 0},
|
||||
{ 0, 0, 0, 0, 0}
|
||||
},
|
||||
1,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::SOBEL_GY
|
||||
{
|
||||
{ { 1, 2, 4, 2, 1},
|
||||
{-1, -1, 0, 1, 1},
|
||||
{-2, -2, 0, 2, 2},
|
||||
{-1, -1, 0, 1, 1},
|
||||
{-1, -2, -4, -2, -1},
|
||||
},
|
||||
1,
|
||||
0
|
||||
},
|
||||
|
||||
// IMAGE_FILTER::BLUR_3X3
|
||||
{
|
||||
{ { 0, 0, 0, 0, 0},
|
||||
{ 0, 1, 2, 1, 0},
|
||||
{ 0, 2, 4, 2, 0},
|
||||
{ 0, 1, 2, 1, 0},
|
||||
{ 0, 0, 0, 0, 0},
|
||||
},
|
||||
16,
|
||||
0
|
||||
}
|
||||
};// Filters
|
||||
// clang-format on
|
||||
|
||||
|
||||
/// @todo: This function can be optimized slipping it between the edges and
|
||||
/// do it without use the getpixel function.
|
||||
/// Optimization can be done to m_pixels[ix + iy * m_width]
|
||||
/// but keep in mind the parallel process of the algorithm
|
||||
void IMAGE::EfxFilter( IMAGE* aInImg, IMAGE_FILTER aFilterType )
|
||||
{
|
||||
S_FILTER filter = FILTERS[static_cast<int>( aFilterType )];
|
||||
|
||||
aInImg->m_wraping = IMAGE_WRAP::CLAMP;
|
||||
m_wraping = IMAGE_WRAP::CLAMP;
|
||||
|
||||
std::atomic<size_t> nextRow( 0 );
|
||||
std::atomic<size_t> threadsFinished( 0 );
|
||||
|
||||
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
|
||||
|
||||
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
|
||||
{
|
||||
std::thread t = std::thread( [&]()
|
||||
{
|
||||
for( size_t iy = nextRow.fetch_add( 1 ); iy < m_height; iy = nextRow.fetch_add( 1 ) )
|
||||
{
|
||||
for( size_t ix = 0; ix < m_width; ix++ )
|
||||
{
|
||||
int v = 0;
|
||||
|
||||
for( size_t sy = 0; sy < 5; sy++ )
|
||||
{
|
||||
for( size_t sx = 0; sx < 5; sx++ )
|
||||
{
|
||||
int factor = filter.kernel[sx][sy];
|
||||
unsigned char pixelv = aInImg->Getpixel( ix + sx - 2, iy + sy - 2 );
|
||||
|
||||
v += pixelv * factor;
|
||||
}
|
||||
}
|
||||
|
||||
v /= filter.div;
|
||||
v += filter.offset;
|
||||
CLAMP(v, 0, 255);
|
||||
|
||||
/// @todo This needs to write to a separate buffer.
|
||||
m_pixels[ix + iy * m_width] = v;
|
||||
}
|
||||
}
|
||||
|
||||
threadsFinished++;
|
||||
} );
|
||||
|
||||
t.detach();
|
||||
}
|
||||
|
||||
while( threadsFinished < parallelThreadCount )
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::EfxFilter_SkipCenter( IMAGE* aInImg, IMAGE_FILTER aFilterType, unsigned int aRadius )
|
||||
{
|
||||
if( ( !aInImg ) || ( m_width != aInImg->m_width ) || ( m_height != aInImg->m_height ) )
|
||||
return;
|
||||
|
||||
S_FILTER filter = FILTERS[static_cast<int>( aFilterType )];
|
||||
|
||||
aInImg->m_wraping = IMAGE_WRAP::ZERO;
|
||||
|
||||
const unsigned int radiusSquared = aRadius * aRadius;
|
||||
|
||||
const unsigned int xCenter = m_width / 2;
|
||||
const unsigned int yCenter = m_height / 2;
|
||||
|
||||
for( size_t iy = 0; iy < m_height; iy++ )
|
||||
{
|
||||
int yc = iy - yCenter;
|
||||
|
||||
unsigned int ycsq = yc * yc;
|
||||
|
||||
for( size_t ix = 0; ix < m_width; ix++ )
|
||||
{
|
||||
int xc = ix - xCenter;
|
||||
|
||||
unsigned int xcsq = xc * xc;
|
||||
|
||||
if( ( xcsq + ycsq ) < radiusSquared )
|
||||
{
|
||||
const unsigned int offset = ix + iy * m_width;
|
||||
|
||||
m_pixels[offset] = aInImg->m_pixels[offset];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int v = 0;
|
||||
|
||||
for( size_t sy = 0; sy < 5; sy++ )
|
||||
{
|
||||
for( size_t sx = 0; sx < 5; sx++ )
|
||||
{
|
||||
int factor = filter.kernel[sx][sy];
|
||||
unsigned char pixelv = aInImg->Getpixel( ix + sx - 2, iy + sy - 2 );
|
||||
|
||||
v += pixelv * factor;
|
||||
}
|
||||
}
|
||||
|
||||
v /= filter.div;
|
||||
v += filter.offset;
|
||||
CLAMP(v, 0, 255);
|
||||
|
||||
m_pixels[ix + iy * m_width] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::SetPixelsFromNormalizedFloat( const float* aNormalizedFloatArray )
|
||||
{
|
||||
for( unsigned int i = 0; i < m_wxh; i++ )
|
||||
{
|
||||
int v = aNormalizedFloatArray[i] * 255;
|
||||
|
||||
CLAMP( v, 0, 255 );
|
||||
m_pixels[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IMAGE::SaveAsPNG( const wxString& aFileName ) const
|
||||
{
|
||||
DBG_SaveBuffer( aFileName, m_pixels, m_width, m_height );
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file image.h
|
||||
* @brief one 8bit-channel image definition.
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_H
|
||||
#define IMAGE_H
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
/// Image operation type
|
||||
enum class IMAGE_OP
|
||||
{
|
||||
RAW,
|
||||
ADD,
|
||||
SUB,
|
||||
DIF,
|
||||
MUL,
|
||||
AND,
|
||||
OR,
|
||||
XOR,
|
||||
BLEND50,
|
||||
MIN,
|
||||
MAX
|
||||
};
|
||||
|
||||
|
||||
/// Image wrap type enumeration
|
||||
enum class IMAGE_WRAP
|
||||
{
|
||||
ZERO, ///< Coords that wraps are not evaluated
|
||||
CLAMP, ///< Coords are clamped to image size
|
||||
WRAP ///< Coords are wrapped around
|
||||
};
|
||||
|
||||
|
||||
/// Filter type enumeration
|
||||
enum class IMAGE_FILTER
|
||||
{
|
||||
HIPASS,
|
||||
GAUSSIAN_BLUR,
|
||||
GAUSSIAN_BLUR2,
|
||||
INVERT_BLUR,
|
||||
CARTOON,
|
||||
EMBOSS,
|
||||
SHARPEN,
|
||||
MELT,
|
||||
SOBEL_GX,
|
||||
SOBEL_GY,
|
||||
BLUR_3X3,
|
||||
};
|
||||
|
||||
/// 5x5 Filter struct parameters
|
||||
struct S_FILTER
|
||||
{
|
||||
signed char kernel[5][5];
|
||||
unsigned int div;
|
||||
unsigned char offset;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Manage an 8-bit channel image.
|
||||
*/
|
||||
class IMAGE
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a IMAGE based on image size.
|
||||
*
|
||||
* @param aXsize x size
|
||||
* @param aYsize y size
|
||||
*/
|
||||
IMAGE( unsigned int aXsize, unsigned int aYsize );
|
||||
|
||||
/**
|
||||
* Construct a IMAGE based from an existing image.
|
||||
*
|
||||
* It will make a copy the \a aSrcImage.
|
||||
*
|
||||
* @param aSrcImage
|
||||
*/
|
||||
IMAGE( const IMAGE& aSrcImage );
|
||||
|
||||
~IMAGE();
|
||||
|
||||
/**
|
||||
* Set a value in a pixel position, position is clamped in accordance with the
|
||||
* current clamp settings.
|
||||
*
|
||||
* @param aX x position
|
||||
* @param aY y position
|
||||
* @param aValue value to set the pixel
|
||||
*/
|
||||
void Setpixel( int aX, int aY, unsigned char aValue );
|
||||
|
||||
/**
|
||||
* Get the pixel value from pixel position, position is clamped in accord with the
|
||||
* current clamp settings.
|
||||
*
|
||||
* @param aX x position
|
||||
* @param aY y position
|
||||
* @return unsigned char - pixel value
|
||||
*/
|
||||
unsigned char Getpixel( int aX, int aY ) const;
|
||||
|
||||
/**
|
||||
* Draw a horizontal line.
|
||||
*
|
||||
* @param aXStart x start position
|
||||
* @param aXEnd x end position
|
||||
* @param aY y position
|
||||
* @param aValue value to add
|
||||
*/
|
||||
void Hline( int aXStart, int aXEnd, int aY, unsigned char aValue );
|
||||
|
||||
void CircleFilled( int aCx, int aCy, int aRadius, unsigned char aValue );
|
||||
|
||||
/**
|
||||
* Perform a copy operation based on \a aOperation type.
|
||||
*
|
||||
* The available image operations.
|
||||
* - IMAGE_OP::RAW this <- aImgA
|
||||
* - IMAGE_OP::ADD this <- CLAMP(aImgA + aImgB)
|
||||
* - IMAGE_OP::SUB this <- CLAMP(aImgA - aImgB)
|
||||
* - IMAGE_OP::DIF this <- abs(aImgA - aImgB)
|
||||
* - IMAGE_OP::MUL this <- aImgA * aImgB
|
||||
* - IMAGE_OP::AND this <- aImgA & aImgB
|
||||
* - IMAGE_OP::OR this <- aImgA | aImgB
|
||||
* - IMAGE_OP::XOR this <- aImgA ^ aImgB
|
||||
* - IMAGE_OP::BLEND50 this <- (aImgA + aImgB) / 2
|
||||
* - IMAGE_OP::MIN this <- (aImgA < aImgB) ? aImgA : aImgB
|
||||
* - IMAGE_OP::MAX this <- (aImgA > aImgB) ? aImgA : aImgB
|
||||
*
|
||||
* @param aImgA an image input.
|
||||
* @param aImgB an image input.
|
||||
* @param aOperation operation to perform
|
||||
*/
|
||||
void CopyFull( const IMAGE* aImgA, const IMAGE* aImgB, IMAGE_OP aOperation );
|
||||
|
||||
/**
|
||||
* Invert the values of this image <- (255 - this)
|
||||
*/
|
||||
void Invert();
|
||||
|
||||
/**
|
||||
* Apply a filter to the input image and store it in the image class.
|
||||
*
|
||||
* @param aInImg input image
|
||||
* @param aFilterType filter type to apply
|
||||
*/
|
||||
void EfxFilter( IMAGE* aInImg, IMAGE_FILTER aFilterType );
|
||||
|
||||
/**
|
||||
* Apply a filter to the input image and store it in the image class.
|
||||
* skip the circle center defined by radius
|
||||
*
|
||||
* @param aInImg input image
|
||||
* @param aFilterType filter type to apply
|
||||
* @param aRadius center circle that the effect will not be applied
|
||||
*/
|
||||
void EfxFilter_SkipCenter( IMAGE* aInImg, IMAGE_FILTER aFilterType, unsigned int aRadius );
|
||||
|
||||
/**
|
||||
* Save image buffer to a PNG file into the working folder.
|
||||
*
|
||||
* Each RGB channel will have the 8bit-channel from the image.
|
||||
*
|
||||
* @param aFileName file name (without extension)
|
||||
*/
|
||||
void SaveAsPNG( const wxString& aFileName ) const;
|
||||
|
||||
/**
|
||||
* Set the current channel from a float normalized (0.0 - 1.0) buffer.
|
||||
*
|
||||
* this <- CLAMP(NormalizedFloat * 255)
|
||||
*
|
||||
* @param aNormalizedFloatArray a float array with the same size of the image
|
||||
*/
|
||||
void SetPixelsFromNormalizedFloat( const float* aNormalizedFloatArray );
|
||||
|
||||
/**
|
||||
* Get the image buffer pointer.
|
||||
*
|
||||
* @return unsigned char* the pointer of the buffer 8bit channel.
|
||||
*/
|
||||
unsigned char* GetBuffer() const;
|
||||
|
||||
unsigned int GetWidth() const { return m_width; }
|
||||
unsigned int GetHeight() const { return m_height; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calculate the coordinates points in accord with the current clamping settings.
|
||||
*
|
||||
* @param aXo X coordinate to be converted (output).
|
||||
* @param aXo Y coordinate to be converted (output).
|
||||
* @return bool - true if the coordinates are inside the image, false otherwise.
|
||||
*/
|
||||
bool wrapCoords( int* aXo, int* aYo ) const;
|
||||
|
||||
void plot8CircleLines( int aCx, int aCy, int aX, int aY, unsigned char aValue );
|
||||
|
||||
unsigned char* m_pixels; ///< buffer to store the image 8bit-channel
|
||||
unsigned int m_width; ///< width of the image
|
||||
unsigned int m_height; ///< height of the image
|
||||
unsigned int m_wxh; ///< width * height precalc value
|
||||
IMAGE_WRAP m_wraping; ///< current wrapping type
|
||||
};
|
||||
|
||||
#endif // IMAGE_H
|
||||
@@ -1,529 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2020 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
* Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file c_ogl_3dmodel.cpp
|
||||
* @brief
|
||||
*/
|
||||
#include <stdexcept>
|
||||
#include <gal/opengl/kiglew.h> // Must be included first
|
||||
|
||||
#include "3d_model.h"
|
||||
#include "opengl_utils.h"
|
||||
#include "../common_ogl/ogl_utils.h"
|
||||
#include "../3d_math.h"
|
||||
#include <wx/debug.h>
|
||||
#include <wx/log.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
|
||||
/*
|
||||
* Flag to enable connectivity profiling.
|
||||
*
|
||||
* @ingroup trace_env_vars
|
||||
*/
|
||||
const wxChar* MODEL_3D::m_logTrace = wxT( "KI_TRACE_EDA_OGL_3DMODEL" );
|
||||
|
||||
|
||||
void MODEL_3D::MakeBbox( const BBOX_3D& aBox, unsigned int aIdxOffset, VERTEX* aVtxOut,
|
||||
GLuint* aIdxOut, const glm::vec4& aColor )
|
||||
{
|
||||
aVtxOut[0].m_pos = { aBox.Min().x, aBox.Min().y, aBox.Min().z };
|
||||
aVtxOut[1].m_pos = { aBox.Max().x, aBox.Min().y, aBox.Min().z };
|
||||
aVtxOut[2].m_pos = { aBox.Max().x, aBox.Max().y, aBox.Min().z };
|
||||
aVtxOut[3].m_pos = { aBox.Min().x, aBox.Max().y, aBox.Min().z };
|
||||
|
||||
aVtxOut[4].m_pos = { aBox.Min().x, aBox.Min().y, aBox.Max().z };
|
||||
aVtxOut[5].m_pos = { aBox.Max().x, aBox.Min().y, aBox.Max().z };
|
||||
aVtxOut[6].m_pos = { aBox.Max().x, aBox.Max().y, aBox.Max().z };
|
||||
aVtxOut[7].m_pos = { aBox.Min().x, aBox.Max().y, aBox.Max().z };
|
||||
|
||||
for( unsigned int i = 0; i < 8; ++i )
|
||||
aVtxOut[i].m_color = aVtxOut[i].m_cad_color = glm::clamp( aColor * 255.0f, 0.0f, 255.0f );
|
||||
|
||||
#define bbox_line( vtx_a, vtx_b )\
|
||||
do { *aIdxOut++ = vtx_a + aIdxOffset; \
|
||||
*aIdxOut++ = vtx_b + aIdxOffset; } while( 0 )
|
||||
|
||||
bbox_line( 0, 1 );
|
||||
bbox_line( 1, 2 );
|
||||
bbox_line( 2, 3 );
|
||||
bbox_line( 3, 0 );
|
||||
|
||||
bbox_line( 4, 5 );
|
||||
bbox_line( 5, 6 );
|
||||
bbox_line( 6, 7 );
|
||||
bbox_line( 7, 4 );
|
||||
|
||||
bbox_line( 0, 4 );
|
||||
bbox_line( 1, 5 );
|
||||
bbox_line( 2, 6 );
|
||||
bbox_line( 3, 7 );
|
||||
|
||||
#undef bbox_line
|
||||
}
|
||||
|
||||
|
||||
MODEL_3D::MODEL_3D( const S3DMODEL& a3DModel, MATERIAL_MODE aMaterialMode )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "MODEL_3D::MODEL_3D %u meshes %u materials" ),
|
||||
static_cast<unsigned int>( a3DModel.m_MeshesSize ),
|
||||
static_cast<unsigned int>( a3DModel.m_MaterialsSize ) );
|
||||
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
GLuint buffers[8];
|
||||
|
||||
/**
|
||||
* WARNING: Horrible hack here!
|
||||
* Somehow, buffer values are being shared between pcbnew and the 3d viewer, which then frees
|
||||
* the buffer, resulting in errors in pcbnew. To resolve this temporarily, we generate
|
||||
* extra buffers in 3dviewer and use the higher numbers. These are freed on close.
|
||||
* todo: Correctly separate the OpenGL contexts to prevent overlapping buffer vals
|
||||
*/
|
||||
glGenBuffers( 6, buffers );
|
||||
m_bbox_vertex_buffer = buffers[2];
|
||||
m_bbox_index_buffer = buffers[3];
|
||||
m_vertex_buffer = buffers[4];
|
||||
m_index_buffer = buffers[5];
|
||||
|
||||
// Validate a3DModel pointers
|
||||
wxASSERT( a3DModel.m_Materials != nullptr );
|
||||
wxASSERT( a3DModel.m_Meshes != nullptr );
|
||||
wxASSERT( a3DModel.m_MaterialsSize > 0 );
|
||||
wxASSERT( a3DModel.m_MeshesSize > 0 );
|
||||
|
||||
m_materialMode = aMaterialMode;
|
||||
|
||||
if( a3DModel.m_Materials == nullptr || a3DModel.m_Meshes == nullptr
|
||||
|| a3DModel.m_MaterialsSize == 0 || a3DModel.m_MeshesSize == 0 )
|
||||
return;
|
||||
|
||||
// create empty bbox for each mesh. it will be updated when the vertices are copied.
|
||||
m_meshes_bbox.resize( a3DModel.m_MeshesSize );
|
||||
|
||||
// copy materials for later use during rendering.
|
||||
m_materials.reserve( a3DModel.m_MaterialsSize );
|
||||
|
||||
for( unsigned int i = 0; i < a3DModel.m_MaterialsSize; ++i )
|
||||
m_materials.emplace_back( a3DModel.m_Materials[i] );
|
||||
|
||||
// build temporary vertex and index buffers for bounding boxes.
|
||||
// the first box is the outer box.
|
||||
std::vector<VERTEX> bbox_tmp_vertices( ( m_meshes_bbox.size() + 1 ) * bbox_vtx_count );
|
||||
std::vector<GLuint> bbox_tmp_indices( ( m_meshes_bbox.size() + 1 ) * bbox_idx_count );
|
||||
|
||||
// group all meshes by material.
|
||||
// for each material create a combined vertex and index buffer.
|
||||
// some models might have many sub-meshes. so iterate over the
|
||||
// input meshes only once.
|
||||
struct MESH_GROUP
|
||||
{
|
||||
std::vector<VERTEX> m_vertices;
|
||||
std::vector<GLuint> m_indices;
|
||||
};
|
||||
|
||||
std::vector<MESH_GROUP> mesh_groups( m_materials.size() );
|
||||
|
||||
for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i )
|
||||
{
|
||||
const SMESH& mesh = a3DModel.m_Meshes[mesh_i];
|
||||
|
||||
// silently ignore meshes that have invalid material references or invalid geometry.
|
||||
if( mesh.m_MaterialIdx >= m_materials.size()
|
||||
|| mesh.m_Positions == nullptr
|
||||
|| mesh.m_FaceIdx == nullptr
|
||||
|| mesh.m_Normals == nullptr
|
||||
|| mesh.m_FaceIdxSize == 0
|
||||
|| mesh.m_VertexSize == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MESH_GROUP& mesh_group = mesh_groups[mesh.m_MaterialIdx];
|
||||
MATERIAL& material = m_materials[mesh.m_MaterialIdx];
|
||||
|
||||
if( material.IsTransparent() && m_materialMode != MATERIAL_MODE::DIFFUSE_ONLY )
|
||||
m_have_transparent_meshes = true;
|
||||
else
|
||||
m_have_opaque_meshes = true;
|
||||
|
||||
const unsigned int vtx_offset = mesh_group.m_vertices.size();
|
||||
mesh_group.m_vertices.resize( mesh_group.m_vertices.size() + mesh.m_VertexSize );
|
||||
|
||||
// copy vertex data and update the bounding box.
|
||||
// use material color for mesh bounding box or some sort of average vertex color.
|
||||
glm::vec3 avg_color = material.m_Diffuse;
|
||||
|
||||
for( unsigned int vtx_i = 0; vtx_i < mesh.m_VertexSize; ++vtx_i )
|
||||
{
|
||||
m_meshes_bbox[mesh_i].Union( mesh.m_Positions[vtx_i] );
|
||||
|
||||
VERTEX& vtx_out = mesh_group.m_vertices[vtx_offset + vtx_i];
|
||||
|
||||
vtx_out.m_pos = mesh.m_Positions[vtx_i];
|
||||
vtx_out.m_nrm = glm::clamp( glm::vec4( mesh.m_Normals[vtx_i], 1.0f ) * 127.0f,
|
||||
-127.0f, 127.0f );
|
||||
|
||||
vtx_out.m_tex_uv = mesh.m_Texcoords != nullptr ? mesh.m_Texcoords[vtx_i]
|
||||
: glm::vec2 (0);
|
||||
|
||||
if( mesh.m_Color != nullptr )
|
||||
{
|
||||
avg_color = ( avg_color + mesh.m_Color[vtx_i] ) * 0.5f;
|
||||
|
||||
vtx_out.m_color =
|
||||
glm::clamp( glm::vec4( mesh.m_Color[vtx_i],
|
||||
1 - material.m_Transparency ) * 255.0f,
|
||||
0.0f, 255.0f );
|
||||
|
||||
vtx_out.m_cad_color =
|
||||
glm::clamp( glm::vec4( MaterialDiffuseToColorCAD( mesh.m_Color[vtx_i] ),
|
||||
1 ) * 255.0f, 0.0f, 255.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
// the mesh will be rendered with other meshes that might have
|
||||
// vertex colors. thus, we can't enable/disable vertex colors
|
||||
// for individual meshes during rendering.
|
||||
|
||||
// if there are no vertex colors, use material color instead.
|
||||
vtx_out.m_color =
|
||||
glm::clamp( glm::vec4( material.m_Diffuse,
|
||||
1 - material.m_Transparency ) * 255.0f,
|
||||
0.0f, 255.0f );
|
||||
|
||||
vtx_out.m_cad_color =
|
||||
glm::clamp( glm::vec4 ( MaterialDiffuseToColorCAD( material.m_Diffuse ),
|
||||
1 ) * 255.0f,
|
||||
0.0f, 255.0f );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_meshes_bbox[mesh_i].IsInitialized() )
|
||||
{
|
||||
// generate geometry for the bounding box
|
||||
MakeBbox( m_meshes_bbox[mesh_i], ( mesh_i + 1 ) * bbox_vtx_count,
|
||||
&bbox_tmp_vertices[( mesh_i + 1 ) * bbox_vtx_count],
|
||||
&bbox_tmp_indices[( mesh_i + 1 ) * bbox_idx_count],
|
||||
{ avg_color, 1.0f } );
|
||||
|
||||
// bump the outer bounding box
|
||||
m_model_bbox.Union( m_meshes_bbox[mesh_i] );
|
||||
}
|
||||
|
||||
|
||||
// append indices of this mesh to the mesh group.
|
||||
const unsigned int idx_offset = mesh_group.m_indices.size();
|
||||
unsigned int use_idx_count = mesh.m_FaceIdxSize;
|
||||
|
||||
if( use_idx_count % 3 != 0 )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( " index count %u not multiple of 3, truncating" ),
|
||||
static_cast<unsigned int>( use_idx_count ) );
|
||||
use_idx_count = ( use_idx_count / 3 ) * 3;
|
||||
}
|
||||
|
||||
mesh_group.m_indices.resize( mesh_group.m_indices.size() + use_idx_count );
|
||||
|
||||
for( unsigned int idx_i = 0; idx_i < use_idx_count; ++idx_i )
|
||||
{
|
||||
if( mesh.m_FaceIdx[idx_i] >= mesh.m_VertexSize )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( " index %u out of range (%u)" ),
|
||||
static_cast<unsigned int>( mesh.m_FaceIdx[idx_i] ),
|
||||
static_cast<unsigned int>( mesh.m_VertexSize ) );
|
||||
|
||||
// FIXME: should skip this triangle
|
||||
}
|
||||
|
||||
mesh_group.m_indices[idx_offset + idx_i] = mesh.m_FaceIdx[idx_i] + vtx_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// generate geometry for the outer bounding box
|
||||
if( m_model_bbox.IsInitialized() )
|
||||
MakeBbox( m_model_bbox, 0, &bbox_tmp_vertices[0], &bbox_tmp_indices[0],
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f } );
|
||||
|
||||
// create bounding box buffers
|
||||
glGenBuffers( 1, &m_bbox_vertex_buffer );
|
||||
glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
|
||||
glBufferData( GL_ARRAY_BUFFER, sizeof( VERTEX ) * bbox_tmp_vertices.size(),
|
||||
bbox_tmp_vertices.data(), GL_STATIC_DRAW );
|
||||
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
|
||||
|
||||
if( bbox_tmp_vertices.size() <= std::numeric_limits<GLushort>::max() )
|
||||
{
|
||||
m_bbox_index_buffer_type = GL_UNSIGNED_SHORT;
|
||||
|
||||
auto u16buf = std::make_unique<GLushort[]>( bbox_tmp_indices.size() );
|
||||
|
||||
for( unsigned int i = 0; i < bbox_tmp_indices.size(); ++i )
|
||||
u16buf[i] = static_cast<GLushort>( bbox_tmp_indices[i] );
|
||||
|
||||
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( GLushort ) * bbox_tmp_indices.size(),
|
||||
u16buf.get(), GL_STATIC_DRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bbox_index_buffer_type = GL_UNSIGNED_INT;
|
||||
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( GLuint ) * bbox_tmp_indices.size(),
|
||||
bbox_tmp_indices.data(), GL_STATIC_DRAW );
|
||||
}
|
||||
|
||||
// merge the mesh group geometry data.
|
||||
unsigned int total_vertex_count = 0;
|
||||
unsigned int total_index_count = 0;
|
||||
|
||||
for( const MESH_GROUP& mg : mesh_groups )
|
||||
{
|
||||
total_vertex_count += mg.m_vertices.size();
|
||||
total_index_count += mg.m_indices.size();
|
||||
}
|
||||
|
||||
wxLogTrace( m_logTrace, wxT( " total %u vertices, %u indices" ),
|
||||
total_vertex_count, total_index_count );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, m_vertex_buffer );
|
||||
glBufferData( GL_ARRAY_BUFFER, sizeof( VERTEX ) * total_vertex_count,
|
||||
nullptr, GL_STATIC_DRAW );
|
||||
|
||||
unsigned int idx_size = 0;
|
||||
|
||||
if( total_vertex_count <= std::numeric_limits<GLushort>::max() )
|
||||
{
|
||||
m_index_buffer_type = GL_UNSIGNED_SHORT;
|
||||
idx_size = sizeof( GLushort );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_index_buffer_type = GL_UNSIGNED_INT;
|
||||
idx_size = sizeof( GLuint );
|
||||
}
|
||||
|
||||
// temporary index buffer which will contain either GLushort or GLuint
|
||||
// type indices. allocate with a bit of meadow at the end.
|
||||
auto tmp_idx = std::make_unique<GLuint[]>( ( idx_size * total_index_count + 8 ) / sizeof( GLuint ) );
|
||||
|
||||
unsigned int prev_vtx_count = 0;
|
||||
unsigned int idx_offset = 0;
|
||||
unsigned int vtx_offset = 0;
|
||||
|
||||
for( unsigned int mg_i = 0; mg_i < mesh_groups.size (); ++mg_i )
|
||||
{
|
||||
MESH_GROUP& mg = mesh_groups[mg_i];
|
||||
MATERIAL& mat = m_materials[mg_i];
|
||||
uintptr_t tmp_idx_ptr = reinterpret_cast<uintptr_t>( tmp_idx.get() );
|
||||
|
||||
if( m_index_buffer_type == GL_UNSIGNED_SHORT )
|
||||
{
|
||||
GLushort* idx_out = reinterpret_cast<GLushort*>( tmp_idx_ptr + idx_offset );
|
||||
|
||||
for( GLuint idx : mg.m_indices )
|
||||
*idx_out++ = static_cast<GLushort>( idx + prev_vtx_count );
|
||||
}
|
||||
else if( m_index_buffer_type == GL_UNSIGNED_INT )
|
||||
{
|
||||
GLuint* idx_out = reinterpret_cast<GLuint*>( tmp_idx_ptr + idx_offset );
|
||||
|
||||
for( GLuint idx : mg.m_indices )
|
||||
*idx_out++ = static_cast<GLuint>( idx + prev_vtx_count );
|
||||
}
|
||||
|
||||
glBufferSubData( GL_ARRAY_BUFFER, vtx_offset, mg.m_vertices.size() * sizeof( VERTEX ),
|
||||
mg.m_vertices.data() );
|
||||
|
||||
mat.m_render_idx_buffer_offset = idx_offset;
|
||||
mat.m_render_idx_count = mg.m_indices.size();
|
||||
|
||||
prev_vtx_count += mg.m_vertices.size();
|
||||
idx_offset += mg.m_indices.size() * idx_size;
|
||||
vtx_offset += mg.m_vertices.size() * sizeof( VERTEX );
|
||||
}
|
||||
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_index_buffer );
|
||||
glBufferData( GL_ELEMENT_ARRAY_BUFFER, idx_size * total_index_count, tmp_idx.get(),
|
||||
GL_STATIC_DRAW );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
|
||||
auto end_time = std::chrono::high_resolution_clock::now();
|
||||
|
||||
wxLogTrace( m_logTrace, wxT( " loaded in %u ms\n" ),
|
||||
(unsigned int)std::chrono::duration_cast<std::chrono::milliseconds> (
|
||||
end_time - start_time).count() );
|
||||
}
|
||||
|
||||
|
||||
void MODEL_3D::BeginDrawMulti( bool aUseColorInformation )
|
||||
{
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glEnableClientState( GL_NORMAL_ARRAY );
|
||||
|
||||
if( aUseColorInformation )
|
||||
{
|
||||
glEnableClientState( GL_COLOR_ARRAY );
|
||||
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glEnable( GL_COLOR_MATERIAL );
|
||||
}
|
||||
|
||||
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
||||
}
|
||||
|
||||
|
||||
void MODEL_3D::EndDrawMulti()
|
||||
{
|
||||
glDisable( GL_COLOR_MATERIAL );
|
||||
glDisableClientState( GL_VERTEX_ARRAY );
|
||||
glDisableClientState( GL_NORMAL_ARRAY );
|
||||
glDisableClientState( GL_COLOR_ARRAY );
|
||||
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
|
||||
|
||||
void MODEL_3D::Draw( bool aTransparent, float aOpacity, bool aUseSelectedMaterial,
|
||||
SFVEC3F& aSelectionColor ) const
|
||||
{
|
||||
if( aOpacity <= FLT_EPSILON )
|
||||
return;
|
||||
|
||||
if( !glBindBuffer )
|
||||
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw" );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, m_vertex_buffer );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_index_buffer );
|
||||
|
||||
glVertexPointer( 3, GL_FLOAT, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_pos ) ) );
|
||||
|
||||
glNormalPointer( GL_BYTE, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_nrm ) ) );
|
||||
|
||||
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( m_materialMode == MATERIAL_MODE::CAD_MODE
|
||||
? offsetof( VERTEX, m_cad_color )
|
||||
: offsetof( VERTEX, m_color ) ) );
|
||||
|
||||
glTexCoordPointer( 2, GL_FLOAT, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_tex_uv ) ) );
|
||||
|
||||
const SFVEC4F param = SFVEC4F( 1.0f, 1.0f, 1.0f, aOpacity );
|
||||
|
||||
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (const float*)¶m.x );
|
||||
|
||||
for( const MODEL_3D::MATERIAL& mat : m_materials )
|
||||
{
|
||||
if( ( mat.IsTransparent() != aTransparent )
|
||||
&& ( aOpacity >= 1.0f )
|
||||
&& m_materialMode != MATERIAL_MODE::DIFFUSE_ONLY )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch( m_materialMode )
|
||||
{
|
||||
case MATERIAL_MODE::NORMAL:
|
||||
OglSetMaterial( mat, aOpacity, aUseSelectedMaterial, aSelectionColor );
|
||||
break;
|
||||
|
||||
case MATERIAL_MODE::DIFFUSE_ONLY:
|
||||
OglSetDiffuseMaterial( mat.m_Diffuse, aOpacity, aUseSelectedMaterial, aSelectionColor );
|
||||
break;
|
||||
|
||||
case MATERIAL_MODE::CAD_MODE:
|
||||
OglSetDiffuseMaterial( MaterialDiffuseToColorCAD( mat.m_Diffuse ), aOpacity,
|
||||
aUseSelectedMaterial, aSelectionColor );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
glDrawElements( GL_TRIANGLES, mat.m_render_idx_count, m_index_buffer_type,
|
||||
reinterpret_cast<const void*>(
|
||||
static_cast<uintptr_t>( mat.m_render_idx_buffer_offset ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MODEL_3D::~MODEL_3D()
|
||||
{
|
||||
if( glDeleteBuffers )
|
||||
{
|
||||
glDeleteBuffers( 1, &m_vertex_buffer );
|
||||
glDeleteBuffers( 1, &m_index_buffer );
|
||||
glDeleteBuffers( 1, &m_bbox_vertex_buffer );
|
||||
glDeleteBuffers( 1, &m_bbox_index_buffer );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MODEL_3D::DrawBbox() const
|
||||
{
|
||||
if( !glBindBuffer )
|
||||
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw bbox" );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
|
||||
|
||||
glVertexPointer( 3, GL_FLOAT, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_pos ) ) );
|
||||
|
||||
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_color ) ) );
|
||||
|
||||
glDrawElements( GL_LINES, bbox_idx_count, m_bbox_index_buffer_type,
|
||||
reinterpret_cast<const void*>( 0 ) );
|
||||
}
|
||||
|
||||
|
||||
void MODEL_3D::DrawBboxes() const
|
||||
{
|
||||
if( !glBindBuffer )
|
||||
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw bboxes" );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
|
||||
|
||||
glVertexPointer( 3, GL_FLOAT, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_pos ) ) );
|
||||
|
||||
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( VERTEX ),
|
||||
reinterpret_cast<const void*>( offsetof( VERTEX, m_color ) ) );
|
||||
|
||||
unsigned int idx_size = m_bbox_index_buffer_type == GL_UNSIGNED_SHORT ? sizeof( GLushort )
|
||||
: sizeof( GLuint );
|
||||
|
||||
glDrawElements( GL_LINES, bbox_idx_count * m_meshes_bbox.size(), m_bbox_index_buffer_type,
|
||||
reinterpret_cast<const void*>(
|
||||
static_cast<uintptr_t>( bbox_idx_count * idx_size ) ) );
|
||||
}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2020 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef _MODEL_3D_H_
|
||||
#define _MODEL_3D_H_
|
||||
|
||||
#include <vector>
|
||||
#include <plugins/3dapi/c3dmodel.h>
|
||||
#include "../../common_ogl/openGL_includes.h"
|
||||
#include "../raytracing/shapes3D/bbox_3d.h"
|
||||
#include "../../3d_enums.h"
|
||||
|
||||
#include <wx/chartype.h>
|
||||
|
||||
class MODEL_3D
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Load a 3D model.
|
||||
*
|
||||
* @note This must be called inside a gl context.
|
||||
*
|
||||
* @param a3DModel a 3d model data to load.
|
||||
* @param aMaterialMode a mode to render the materials of the model.
|
||||
*/
|
||||
MODEL_3D( const S3DMODEL& a3DModel, MATERIAL_MODE aMaterialMode );
|
||||
|
||||
~MODEL_3D();
|
||||
|
||||
/**
|
||||
* Render the model into the current context.
|
||||
*/
|
||||
void DrawOpaque( bool aUseSelectedMaterial, SFVEC3F aSelectionColor = SFVEC3F( 0.0f ) ) const
|
||||
{
|
||||
Draw( false, 1.0f, aUseSelectedMaterial, aSelectionColor );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the model into the current context.
|
||||
*/
|
||||
void DrawTransparent( float aOpacity, bool aUseSelectedMaterial,
|
||||
SFVEC3F aSelectionColor = SFVEC3F( 0.0f ) ) const
|
||||
{
|
||||
Draw( true, aOpacity, aUseSelectedMaterial, aSelectionColor );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if have opaque meshes to render.
|
||||
*/
|
||||
bool HasOpaqueMeshes() const { return m_have_opaque_meshes; }
|
||||
|
||||
/**
|
||||
* Return true if have transparent mesh's to render.
|
||||
*/
|
||||
bool HasTransparentMeshes() const { return m_have_transparent_meshes; }
|
||||
|
||||
/**
|
||||
* Draw main bounding box of the model.
|
||||
*/
|
||||
void DrawBbox() const;
|
||||
|
||||
/**
|
||||
* Draw individual bounding boxes of each mesh.
|
||||
*/
|
||||
void DrawBboxes() const;
|
||||
|
||||
/**
|
||||
* Get the main bounding box.
|
||||
* @return the main model bounding box.
|
||||
*/
|
||||
const BBOX_3D& GetBBox() const { return m_model_bbox; }
|
||||
|
||||
/**
|
||||
* Set some basic render states before drawing multiple models.
|
||||
*/
|
||||
static void BeginDrawMulti( bool aUseColorInformation );
|
||||
|
||||
/**
|
||||
* Cleanup render states after drawing multiple models.
|
||||
*/
|
||||
static void EndDrawMulti();
|
||||
|
||||
private:
|
||||
static const wxChar* m_logTrace;
|
||||
|
||||
// the material mode that was used to generate the rendering data.
|
||||
// FIXME: this can be selected at run-time and does not require re-creation
|
||||
// of the whole model objects.
|
||||
MATERIAL_MODE m_materialMode;
|
||||
|
||||
BBOX_3D m_model_bbox; ///< global bounding box for this model
|
||||
std::vector<BBOX_3D> m_meshes_bbox; ///< individual bbox for each mesh
|
||||
|
||||
// unified vertex format for mesh rendering.
|
||||
struct VERTEX
|
||||
{
|
||||
glm::vec3 m_pos;
|
||||
glm::u8vec4 m_nrm; // only 3 components used
|
||||
glm::u8vec4 m_color; // regular color
|
||||
glm::u8vec4 m_cad_color; // "CAD" mode rendering color
|
||||
glm::vec2 m_tex_uv;
|
||||
};
|
||||
|
||||
// vertex buffer and index buffers that include all the individual meshes
|
||||
// lumped together.
|
||||
GLuint m_vertex_buffer = 0;
|
||||
GLuint m_index_buffer = 0;
|
||||
GLenum m_index_buffer_type = GL_INVALID_ENUM;
|
||||
|
||||
// internal material definition
|
||||
// all meshes are grouped by material for rendering purposes.
|
||||
struct MATERIAL : SMATERIAL
|
||||
{
|
||||
unsigned int m_render_idx_buffer_offset = 0;
|
||||
unsigned int m_render_idx_count = 0;
|
||||
|
||||
MATERIAL( const SMATERIAL& aOther ) : SMATERIAL( aOther ) { }
|
||||
bool IsTransparent() const { return m_Transparency > FLT_EPSILON; }
|
||||
};
|
||||
|
||||
std::vector<MATERIAL> m_materials;
|
||||
|
||||
// a model can consist of transparent and opaque parts. remember which
|
||||
// ones are present during initial buffer and data setup. use it later
|
||||
// during rendering.
|
||||
bool m_have_opaque_meshes = false;
|
||||
bool m_have_transparent_meshes = false;
|
||||
|
||||
// vertex buffer and index buffer for the bounding boxes.
|
||||
// the first box is always the outer box, followed by inner boxes (one for each mesh).
|
||||
static constexpr unsigned int bbox_vtx_count = 8;
|
||||
static constexpr unsigned int bbox_idx_count = 24;
|
||||
|
||||
GLuint m_bbox_vertex_buffer = 0;
|
||||
GLuint m_bbox_index_buffer = 0;
|
||||
GLenum m_bbox_index_buffer_type = GL_INVALID_ENUM;
|
||||
|
||||
static void MakeBbox( const BBOX_3D& aBox, unsigned int aIdxOffset, VERTEX* aVtxOut,
|
||||
GLuint* aIdxOut, const glm::vec4& aColor );
|
||||
|
||||
void Draw( bool aTransparent, float aOpacity, bool aUseSelectedMaterial,
|
||||
SFVEC3F& aSelectionColor ) const;
|
||||
};
|
||||
|
||||
#endif // _MODEL_3D_H_
|
||||
@@ -1,979 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 2015-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "render_3d_opengl.h"
|
||||
#include "opengl_utils.h"
|
||||
#include <board.h>
|
||||
#include <footprint.h>
|
||||
#include "../../3d_math.h"
|
||||
#include <trigo.h>
|
||||
#include <project.h>
|
||||
#include <profile.h> // To use GetRunningMicroSecs or another profiling utility
|
||||
#include <fp_lib_table.h>
|
||||
#include <eda_3d_canvas.h>
|
||||
#include <eda_3d_viewer_frame.h>
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::addObjectTriangles( const FILLED_CIRCLE_2D* aFilledCircle,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer, float aZtop,
|
||||
float aZbot )
|
||||
{
|
||||
const SFVEC2F& center = aFilledCircle->GetCenter();
|
||||
const float radius = aFilledCircle->GetRadius() * 2.0f; // Double because the render triangle
|
||||
|
||||
// This is a small adjustment to the circle texture
|
||||
const float texture_factor = ( 8.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
|
||||
const float f = ( sqrtf( 2.0f ) / 2.0f ) * radius * texture_factor;
|
||||
|
||||
// Top and Bot segments ends are just triangle semi-circles, so need to add it in duplicated.
|
||||
aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZtop ),
|
||||
SFVEC3F( center.x - f, center.y, aZtop ),
|
||||
SFVEC3F( center.x, center.y - f, aZtop ) );
|
||||
|
||||
aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZtop ),
|
||||
SFVEC3F( center.x + f, center.y, aZtop ),
|
||||
SFVEC3F( center.x, center.y + f, aZtop ) );
|
||||
|
||||
aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZbot ),
|
||||
SFVEC3F( center.x + f, center.y, aZbot ),
|
||||
SFVEC3F( center.x, center.y - f, aZbot ) );
|
||||
|
||||
aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZbot ),
|
||||
SFVEC3F( center.x - f, center.y, aZbot ),
|
||||
SFVEC3F( center.x, center.y + f, aZbot ) );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::addObjectTriangles( const POLYGON_4PT_2D* aPoly,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer,
|
||||
float aZtop, float aZbot )
|
||||
{
|
||||
const SFVEC2F& v0 = aPoly->GetV0();
|
||||
const SFVEC2F& v1 = aPoly->GetV1();
|
||||
const SFVEC2F& v2 = aPoly->GetV2();
|
||||
const SFVEC2F& v3 = aPoly->GetV3();
|
||||
|
||||
addTopAndBottomTriangles( aDstLayer, v0, v2, v1, aZtop, aZbot );
|
||||
addTopAndBottomTriangles( aDstLayer, v2, v0, v3, aZtop, aZbot );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::generateRing( const SFVEC2F& aCenter, float aInnerRadius,
|
||||
float aOuterRadius, unsigned int aNr_sides_per_circle,
|
||||
std::vector< SFVEC2F >& aInnerContourResult,
|
||||
std::vector< SFVEC2F >& aOuterContourResult,
|
||||
bool aInvertOrder )
|
||||
{
|
||||
aInnerContourResult.clear();
|
||||
aInnerContourResult.reserve( aNr_sides_per_circle + 2 );
|
||||
|
||||
aOuterContourResult.clear();
|
||||
aOuterContourResult.reserve( aNr_sides_per_circle + 2 );
|
||||
|
||||
const int delta = 3600 / aNr_sides_per_circle;
|
||||
|
||||
for( int ii = 0; ii < 3600; ii += delta )
|
||||
{
|
||||
float angle = (float)( aInvertOrder ? ( 3600 - ii ) : ii )
|
||||
* 2.0f * glm::pi<float>() / 3600.0f;
|
||||
const SFVEC2F rotatedDir = SFVEC2F( cos( angle ), sin( angle ) );
|
||||
|
||||
aInnerContourResult.emplace_back( aCenter.x + rotatedDir.x * aInnerRadius,
|
||||
aCenter.y + rotatedDir.y * aInnerRadius );
|
||||
|
||||
aOuterContourResult.emplace_back( aCenter.x + rotatedDir.x * aOuterRadius,
|
||||
aCenter.y + rotatedDir.y * aOuterRadius );
|
||||
}
|
||||
|
||||
aInnerContourResult.push_back( aInnerContourResult[0] );
|
||||
aOuterContourResult.push_back( aOuterContourResult[0] );
|
||||
|
||||
wxASSERT( aInnerContourResult.size() == aOuterContourResult.size() );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::addObjectTriangles( const RING_2D* aRing, TRIANGLE_DISPLAY_LIST* aDstLayer,
|
||||
float aZtop, float aZbot )
|
||||
{
|
||||
const SFVEC2F& center = aRing->GetCenter();
|
||||
const float inner = aRing->GetInnerRadius();
|
||||
const float outer = aRing->GetOuterRadius();
|
||||
|
||||
std::vector< SFVEC2F > innerContour;
|
||||
std::vector< SFVEC2F > outerContour;
|
||||
|
||||
generateRing( center, inner, outer, m_boardAdapter.GetCircleSegmentCount( outer * 2.0f ),
|
||||
innerContour, outerContour, false );
|
||||
|
||||
// This will add the top and bot quads that will form the approximated ring
|
||||
for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
|
||||
{
|
||||
const SFVEC2F& vi0 = innerContour[i + 0];
|
||||
const SFVEC2F& vi1 = innerContour[i + 1];
|
||||
const SFVEC2F& vo0 = outerContour[i + 0];
|
||||
const SFVEC2F& vo1 = outerContour[i + 1];
|
||||
|
||||
aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
|
||||
SFVEC3F( vi0.x, vi0.y, aZtop ),
|
||||
SFVEC3F( vo0.x, vo0.y, aZtop ),
|
||||
SFVEC3F( vo1.x, vo1.y, aZtop ) );
|
||||
|
||||
aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
|
||||
SFVEC3F( vo1.x, vo1.y, aZbot ),
|
||||
SFVEC3F( vo0.x, vo0.y, aZbot ),
|
||||
SFVEC3F( vi0.x, vi0.y, aZbot ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::addObjectTriangles( const TRIANGLE_2D* aTri,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer,
|
||||
float aZtop, float aZbot )
|
||||
{
|
||||
const SFVEC2F& v1 = aTri->GetP1();
|
||||
const SFVEC2F& v2 = aTri->GetP2();
|
||||
const SFVEC2F& v3 = aTri->GetP3();
|
||||
|
||||
addTopAndBottomTriangles( aDstLayer, v1, v2, v3, aZtop, aZbot );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::addObjectTriangles( const ROUND_SEGMENT_2D* aSeg,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer,
|
||||
float aZtop, float aZbot )
|
||||
{
|
||||
const SFVEC2F& leftStart = aSeg->GetLeftStar();
|
||||
const SFVEC2F& leftEnd = aSeg->GetLeftEnd();
|
||||
const SFVEC2F& leftDir = aSeg->GetLeftDir();
|
||||
|
||||
const SFVEC2F& rightStart = aSeg->GetRightStar();
|
||||
const SFVEC2F& rightEnd = aSeg->GetRightEnd();
|
||||
const SFVEC2F& rightDir = aSeg->GetRightDir();
|
||||
const float radius = aSeg->GetRadius();
|
||||
|
||||
const SFVEC2F& start = aSeg->GetStart();
|
||||
const SFVEC2F& end = aSeg->GetEnd();
|
||||
|
||||
const float texture_factor = ( 12.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
|
||||
const float texture_factorF = ( 6.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
|
||||
|
||||
const float radius_of_the_square = sqrtf( aSeg->GetRadiusSquared() * 2.0f );
|
||||
const float radius_triangle_factor = ( radius_of_the_square - radius ) / radius;
|
||||
|
||||
const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor,
|
||||
rightDir.x * radius * radius_triangle_factor );
|
||||
|
||||
const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor,
|
||||
leftDir.x * radius * radius_triangle_factor );
|
||||
|
||||
// Top end segment triangles (semi-circles)
|
||||
aDstLayer->m_layer_top_segment_ends->AddTriangle(
|
||||
SFVEC3F( rightEnd.x + texture_factor * factorS.x,
|
||||
rightEnd.y + texture_factor * factorS.y,
|
||||
aZtop ),
|
||||
SFVEC3F( leftStart.x + texture_factor * factorE.x,
|
||||
leftStart.y + texture_factor * factorE.y,
|
||||
aZtop ),
|
||||
SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
|
||||
start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
|
||||
aZtop ) );
|
||||
|
||||
aDstLayer->m_layer_top_segment_ends->AddTriangle(
|
||||
SFVEC3F( leftEnd.x + texture_factor * factorE.x,
|
||||
leftEnd.y + texture_factor * factorE.y, aZtop ),
|
||||
SFVEC3F( rightStart.x + texture_factor * factorS.x,
|
||||
rightStart.y + texture_factor * factorS.y, aZtop ),
|
||||
SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
|
||||
end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
|
||||
aZtop ) );
|
||||
|
||||
// Bot end segment triangles (semi-circles)
|
||||
aDstLayer->m_layer_bot_segment_ends->AddTriangle(
|
||||
SFVEC3F( leftStart.x + texture_factor * factorE.x,
|
||||
leftStart.y + texture_factor * factorE.y,
|
||||
aZbot ),
|
||||
SFVEC3F( rightEnd.x + texture_factor * factorS.x,
|
||||
rightEnd.y + texture_factor * factorS.y,
|
||||
aZbot ),
|
||||
SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
|
||||
start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
|
||||
aZbot ) );
|
||||
|
||||
aDstLayer->m_layer_bot_segment_ends->AddTriangle(
|
||||
SFVEC3F( rightStart.x + texture_factor * factorS.x,
|
||||
rightStart.y + texture_factor * factorS.y, aZbot ),
|
||||
SFVEC3F( leftEnd.x + texture_factor * factorE.x,
|
||||
leftEnd.y + texture_factor * factorE.y, aZbot ),
|
||||
SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
|
||||
end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
|
||||
aZbot ) );
|
||||
|
||||
// Segment top and bot planes
|
||||
aDstLayer->m_layer_top_triangles->AddQuad(
|
||||
SFVEC3F( rightEnd.x, rightEnd.y, aZtop ),
|
||||
SFVEC3F( rightStart.x, rightStart.y, aZtop ),
|
||||
SFVEC3F( leftEnd.x, leftEnd.y, aZtop ),
|
||||
SFVEC3F( leftStart.x, leftStart.y, aZtop ) );
|
||||
|
||||
aDstLayer->m_layer_bot_triangles->AddQuad(
|
||||
SFVEC3F( rightEnd.x, rightEnd.y, aZbot ),
|
||||
SFVEC3F( leftStart.x, leftStart.y, aZbot ),
|
||||
SFVEC3F( leftEnd.x, leftEnd.y, aZbot ),
|
||||
SFVEC3F( rightStart.x, rightStart.y, aZbot ) );
|
||||
}
|
||||
|
||||
|
||||
OPENGL_RENDER_LIST* RENDER_3D_OPENGL::generateHoles( const LIST_OBJECT2D& aListHolesObject2d,
|
||||
const SHAPE_POLY_SET& aPoly, float aZtop,
|
||||
float aZbot, bool aInvertFaces,
|
||||
const BVH_CONTAINER_2D* aThroughHoles )
|
||||
{
|
||||
OPENGL_RENDER_LIST* ret = nullptr;
|
||||
|
||||
if( aListHolesObject2d.size() > 0 )
|
||||
{
|
||||
TRIANGLE_DISPLAY_LIST* layerTriangles =
|
||||
new TRIANGLE_DISPLAY_LIST( aListHolesObject2d.size() * 2 );
|
||||
|
||||
// Convert the list of objects(filled circles) to triangle layer structure
|
||||
for( const OBJECT_2D* itemOnLayer : aListHolesObject2d )
|
||||
{
|
||||
const OBJECT_2D* object2d_A = itemOnLayer;
|
||||
|
||||
wxASSERT( ( object2d_A->GetObjectType() == OBJECT_2D_TYPE::FILLED_CIRCLE )
|
||||
|| ( object2d_A->GetObjectType() == OBJECT_2D_TYPE::ROUNDSEG ) );
|
||||
|
||||
switch( object2d_A->GetObjectType() )
|
||||
{
|
||||
case OBJECT_2D_TYPE::FILLED_CIRCLE:
|
||||
addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d_A ),
|
||||
layerTriangles, aZtop, aZbot );
|
||||
break;
|
||||
|
||||
case OBJECT_2D_TYPE::ROUNDSEG:
|
||||
addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d_A ),
|
||||
layerTriangles, aZtop, aZbot );
|
||||
break;
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxT( "RENDER_3D_OPENGL::generateHoles: Object type not implemented" ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: he can have a aListHolesObject2d with holes but without contours
|
||||
// eg: when there are only NPTH on the list and the contours were not added
|
||||
if( aPoly.OutlineCount() > 0 )
|
||||
{
|
||||
layerTriangles->AddToMiddleContourns( aPoly, aZbot, aZtop,
|
||||
m_boardAdapter.BiuTo3dUnits(),
|
||||
aInvertFaces, aThroughHoles );
|
||||
}
|
||||
|
||||
ret = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, aZbot, aZtop );
|
||||
|
||||
delete layerTriangles;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
OPENGL_RENDER_LIST* RENDER_3D_OPENGL::generateLayerList( const BVH_CONTAINER_2D* aContainer,
|
||||
const SHAPE_POLY_SET* aPolyList,
|
||||
PCB_LAYER_ID aLayerId,
|
||||
const BVH_CONTAINER_2D* aThroughHoles )
|
||||
{
|
||||
if( aContainer == nullptr )
|
||||
return nullptr;
|
||||
|
||||
const LIST_OBJECT2D& listObject2d = aContainer->GetList();
|
||||
|
||||
if( listObject2d.size() == 0 )
|
||||
return nullptr;
|
||||
|
||||
float layer_z_bot = 0.0f;
|
||||
float layer_z_top = 0.0f;
|
||||
|
||||
getLayerZPos( aLayerId, layer_z_top, layer_z_bot );
|
||||
|
||||
// Calculate an estimation for the nr of triangles based on the nr of objects
|
||||
unsigned int nrTrianglesEstimation = listObject2d.size() * 8;
|
||||
|
||||
TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( nrTrianglesEstimation );
|
||||
|
||||
// store in a list so it will be latter deleted
|
||||
m_triangles.push_back( layerTriangles );
|
||||
|
||||
// Load the 2D (X,Y axis) component of shapes
|
||||
for( const OBJECT_2D* itemOnLayer : listObject2d )
|
||||
{
|
||||
const OBJECT_2D* object2d_A = itemOnLayer;
|
||||
|
||||
switch( object2d_A->GetObjectType() )
|
||||
{
|
||||
case OBJECT_2D_TYPE::FILLED_CIRCLE:
|
||||
addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d_A ),
|
||||
layerTriangles, layer_z_top, layer_z_bot );
|
||||
break;
|
||||
|
||||
case OBJECT_2D_TYPE::POLYGON4PT:
|
||||
addObjectTriangles( static_cast<const POLYGON_4PT_2D*>( object2d_A ),
|
||||
layerTriangles, layer_z_top, layer_z_bot );
|
||||
break;
|
||||
|
||||
case OBJECT_2D_TYPE::RING:
|
||||
addObjectTriangles( static_cast<const RING_2D*>( object2d_A ),
|
||||
layerTriangles, layer_z_top, layer_z_bot );
|
||||
break;
|
||||
|
||||
case OBJECT_2D_TYPE::TRIANGLE:
|
||||
addObjectTriangles( static_cast<const TRIANGLE_2D*>( object2d_A ),
|
||||
layerTriangles, layer_z_top, layer_z_bot );
|
||||
break;
|
||||
|
||||
case OBJECT_2D_TYPE::ROUNDSEG:
|
||||
addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d_A ),
|
||||
layerTriangles, layer_z_top, layer_z_bot );
|
||||
break;
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxT( "RENDER_3D_OPENGL: Object type is not implemented" ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( aPolyList && aPolyList->OutlineCount() > 0 )
|
||||
{
|
||||
layerTriangles->AddToMiddleContourns( *aPolyList, layer_z_bot, layer_z_top,
|
||||
m_boardAdapter.BiuTo3dUnits(), false, aThroughHoles );
|
||||
}
|
||||
|
||||
// Create display list
|
||||
return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, layer_z_bot, layer_z_top );
|
||||
}
|
||||
|
||||
|
||||
OPENGL_RENDER_LIST* RENDER_3D_OPENGL::generateEmptyLayerList( PCB_LAYER_ID aLayerId )
|
||||
{
|
||||
float layer_z_bot = 0.0f;
|
||||
float layer_z_top = 0.0f;
|
||||
|
||||
getLayerZPos( aLayerId, layer_z_top, layer_z_bot );
|
||||
|
||||
TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( 1 );
|
||||
|
||||
// store in a list so it will be latter deleted
|
||||
m_triangles.push_back( layerTriangles );
|
||||
|
||||
return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, layer_z_bot, layer_z_top );
|
||||
}
|
||||
|
||||
|
||||
OPENGL_RENDER_LIST* RENDER_3D_OPENGL::createBoard( const SHAPE_POLY_SET& aBoardPoly,
|
||||
const BVH_CONTAINER_2D* aThroughHoles )
|
||||
{
|
||||
OPENGL_RENDER_LIST* dispLists = nullptr;
|
||||
CONTAINER_2D boardContainer;
|
||||
|
||||
ConvertPolygonToTriangles( aBoardPoly, boardContainer, m_boardAdapter.BiuTo3dUnits(),
|
||||
(const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
|
||||
|
||||
const LIST_OBJECT2D& listBoardObject2d = boardContainer.GetList();
|
||||
|
||||
if( listBoardObject2d.size() > 0 )
|
||||
{
|
||||
// We will set a unitary Z so it will in future used with transformations since the
|
||||
// board poly will be used not only to draw itself but also the solder mask layers.
|
||||
const float layer_z_top = 1.0f;
|
||||
const float layer_z_bot = 0.0f;
|
||||
|
||||
TRIANGLE_DISPLAY_LIST* layerTriangles =
|
||||
new TRIANGLE_DISPLAY_LIST( listBoardObject2d.size() );
|
||||
|
||||
// Convert the list of objects(triangles) to triangle layer structure
|
||||
for( const OBJECT_2D* itemOnLayer : listBoardObject2d )
|
||||
{
|
||||
const OBJECT_2D* object2d_A = itemOnLayer;
|
||||
|
||||
wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
|
||||
|
||||
const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
|
||||
|
||||
const SFVEC2F& v1 = tri->GetP1();
|
||||
const SFVEC2F& v2 = tri->GetP2();
|
||||
const SFVEC2F& v3 = tri->GetP3();
|
||||
|
||||
addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
|
||||
}
|
||||
|
||||
if( aBoardPoly.OutlineCount() > 0 )
|
||||
{
|
||||
layerTriangles->AddToMiddleContourns( aBoardPoly, layer_z_bot, layer_z_top,
|
||||
m_boardAdapter.BiuTo3dUnits(), false,
|
||||
aThroughHoles );
|
||||
|
||||
dispLists = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
|
||||
layer_z_top, layer_z_top );
|
||||
}
|
||||
|
||||
delete layerTriangles;
|
||||
}
|
||||
|
||||
return dispLists;
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
|
||||
{
|
||||
m_reloadRequested = false;
|
||||
|
||||
freeAllLists();
|
||||
|
||||
OBJECT_2D_STATS::Instance().ResetStats();
|
||||
|
||||
unsigned stats_startReloadTime = GetRunningMicroSecs();
|
||||
|
||||
m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
|
||||
|
||||
SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
|
||||
m_camera.SetBoardLookAtPos( camera_pos );
|
||||
|
||||
if( aStatusReporter )
|
||||
aStatusReporter->Report( _( "Load OpenGL: board" ) );
|
||||
|
||||
// Create Board
|
||||
m_board = createBoard( m_boardAdapter.GetBoardPoly(), &m_boardAdapter.GetThroughHoleIds() );
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.realistic )
|
||||
{
|
||||
m_antiBoardPolys.RemoveAllContours();
|
||||
m_antiBoardPolys.NewOutline();
|
||||
m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, -INT_MAX/2 ) );
|
||||
m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, -INT_MAX/2 ) );
|
||||
m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, INT_MAX/2 ) );
|
||||
m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, INT_MAX/2 ) );
|
||||
m_antiBoardPolys.Outline( 0 ).SetClosed( true );
|
||||
|
||||
m_antiBoardPolys.BooleanSubtract( m_boardAdapter.GetBoardPoly(),
|
||||
SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
m_antiBoard = createBoard( m_antiBoardPolys );
|
||||
}
|
||||
|
||||
SHAPE_POLY_SET board_poly_with_holes = m_boardAdapter.GetBoardPoly().CloneDropTriangulation();
|
||||
board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetOuterNonPlatedThroughHolePoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
m_boardWithHoles = createBoard( board_poly_with_holes );
|
||||
|
||||
if( m_antiBoard )
|
||||
m_antiBoard->SetItIsTransparent( true );
|
||||
|
||||
// Create Through Holes and vias
|
||||
if( aStatusReporter )
|
||||
aStatusReporter->Report( _( "Load OpenGL: holes and vias" ) );
|
||||
|
||||
SHAPE_POLY_SET outerPolyTHT = m_boardAdapter.GetThroughHoleOdPolys().CloneDropTriangulation();
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.realistic )
|
||||
{
|
||||
outerPolyTHT.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
|
||||
SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
}
|
||||
|
||||
m_outerThroughHoles = generateHoles( m_boardAdapter.GetThroughHoleOds().GetList(),
|
||||
outerPolyTHT, 1.0f, 0.0f, false,
|
||||
&m_boardAdapter.GetThroughHoleIds() );
|
||||
|
||||
m_outerViaThroughHoles = generateHoles(
|
||||
m_boardAdapter.GetThroughHoleViaOds().GetList(),
|
||||
m_boardAdapter.GetThroughHoleViaOdPolys(), 1.0f, 0.0f, false );
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.clip_silk_on_via_annulus &&
|
||||
m_boardAdapter.m_Cfg->m_Render.realistic )
|
||||
{
|
||||
m_outerThroughHoleRings = generateHoles(
|
||||
m_boardAdapter.GetThroughHoleAnnularRings().GetList(),
|
||||
m_boardAdapter.GetThroughHoleAnnularRingPolys(), 1.0f, 0.0f, false );
|
||||
}
|
||||
|
||||
const MAP_POLY& innerMapHoles = m_boardAdapter.GetHoleIdPolysMap();
|
||||
const MAP_POLY& outerMapHoles = m_boardAdapter.GetHoleOdPolysMap();
|
||||
|
||||
wxASSERT( innerMapHoles.size() == outerMapHoles.size() );
|
||||
|
||||
const MAP_CONTAINER_2D_BASE& map_holes = m_boardAdapter.GetLayerHoleMap();
|
||||
|
||||
if( outerMapHoles.size() > 0 )
|
||||
{
|
||||
float layer_z_bot = 0.0f;
|
||||
float layer_z_top = 0.0f;
|
||||
|
||||
for( const std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET*>& ii : outerMapHoles )
|
||||
{
|
||||
const PCB_LAYER_ID layer_id = ii.first;
|
||||
const SHAPE_POLY_SET* poly = ii.second;
|
||||
const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
|
||||
|
||||
getLayerZPos( layer_id, layer_z_top, layer_z_bot );
|
||||
|
||||
m_outerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
|
||||
layer_z_top, layer_z_bot, false );
|
||||
}
|
||||
|
||||
for( const std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET*>& ii : innerMapHoles )
|
||||
{
|
||||
const PCB_LAYER_ID layer_id = ii.first;
|
||||
const SHAPE_POLY_SET* poly = ii.second;
|
||||
const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
|
||||
|
||||
getLayerZPos( layer_id, layer_z_top, layer_z_bot );
|
||||
|
||||
m_innerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
|
||||
layer_z_top, layer_z_bot, false );
|
||||
}
|
||||
}
|
||||
|
||||
// Generate vertical cylinders of vias and pads (copper)
|
||||
generateViasAndPads();
|
||||
|
||||
// Add layers maps
|
||||
if( aStatusReporter )
|
||||
aStatusReporter->Report( _( "Load OpenGL: layers" ) );
|
||||
|
||||
const MAP_POLY& map_poly = m_boardAdapter.GetPolyMap();
|
||||
|
||||
for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& ii : m_boardAdapter.GetLayerMap() )
|
||||
{
|
||||
const PCB_LAYER_ID layer_id = ii.first;
|
||||
|
||||
if( !m_boardAdapter.Is3dLayerEnabled( layer_id ) )
|
||||
continue;
|
||||
|
||||
if( aStatusReporter )
|
||||
{
|
||||
aStatusReporter->Report( wxString::Format( _( "Load OpenGL layer %d" ),
|
||||
(int) layer_id ) );
|
||||
}
|
||||
|
||||
const BVH_CONTAINER_2D* container2d = ii.second;
|
||||
|
||||
SHAPE_POLY_SET polyListSubtracted;
|
||||
SHAPE_POLY_SET* polyList = nullptr;
|
||||
|
||||
// Load the vertical (Z axis) component of shapes
|
||||
|
||||
if( map_poly.find( layer_id ) != map_poly.end() )
|
||||
{
|
||||
polyListSubtracted = *map_poly.at( layer_id );;
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.realistic )
|
||||
{
|
||||
polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
if( layer_id != B_Mask && layer_id != F_Mask )
|
||||
{
|
||||
polyListSubtracted.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
polyListSubtracted.BooleanSubtract( m_boardAdapter.GetOuterNonPlatedThroughHolePoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.subtract_mask_from_silk )
|
||||
{
|
||||
if( layer_id == B_SilkS && map_poly.find( B_Mask ) != map_poly.end() )
|
||||
{
|
||||
polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
else if( layer_id == F_SilkS && map_poly.find( F_Mask ) != map_poly.end() )
|
||||
{
|
||||
polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
polyList = &polyListSubtracted;
|
||||
}
|
||||
|
||||
OPENGL_RENDER_LIST* oglList = generateLayerList( container2d, polyList, layer_id,
|
||||
&m_boardAdapter.GetThroughHoleIds() );
|
||||
|
||||
if( oglList != nullptr )
|
||||
m_layers[layer_id] = oglList;
|
||||
|
||||
}
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.renderPlatedPadsAsPlated
|
||||
&& m_boardAdapter.m_Cfg->m_Render.realistic )
|
||||
{
|
||||
const SHAPE_POLY_SET* frontPlatedPadPolys = m_boardAdapter.GetFrontPlatedPadPolys();
|
||||
const SHAPE_POLY_SET* backPlatedPadPolys = m_boardAdapter.GetBackPlatedPadPolys();
|
||||
|
||||
if( frontPlatedPadPolys )
|
||||
{
|
||||
SHAPE_POLY_SET polySubtracted = frontPlatedPadPolys->CloneDropTriangulation();
|
||||
polySubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
polySubtracted.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
polySubtracted.BooleanSubtract( m_boardAdapter.GetOuterNonPlatedThroughHolePoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
m_platedPadsFront = generateLayerList( m_boardAdapter.GetPlatedPadsFront(),
|
||||
&polySubtracted, F_Cu );
|
||||
|
||||
// An entry for F_Cu must exist in m_layers or we'll never look at m_platedPadsFront
|
||||
if( m_layers.count( F_Cu ) == 0 )
|
||||
m_layers[F_Cu] = generateEmptyLayerList( F_Cu );
|
||||
}
|
||||
|
||||
if( backPlatedPadPolys )
|
||||
{
|
||||
SHAPE_POLY_SET polySubtracted = backPlatedPadPolys->CloneDropTriangulation();
|
||||
polySubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
polySubtracted.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
polySubtracted.BooleanSubtract( m_boardAdapter.GetOuterNonPlatedThroughHolePoly(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
m_platedPadsBack = generateLayerList( m_boardAdapter.GetPlatedPadsBack(),
|
||||
&polySubtracted, B_Cu );
|
||||
|
||||
// An entry for B_Cu must exist in m_layers or we'll never look at m_platedPadsBack
|
||||
if( m_layers.count( B_Cu ) == 0 )
|
||||
m_layers[B_Cu] = generateEmptyLayerList( B_Cu );
|
||||
}
|
||||
}
|
||||
|
||||
// Load 3D models
|
||||
if( aStatusReporter )
|
||||
aStatusReporter->Report( _( "Loading 3D models..." ) );
|
||||
|
||||
load3dModels( aStatusReporter );
|
||||
|
||||
if( aStatusReporter )
|
||||
{
|
||||
// Calculation time in seconds
|
||||
double calculation_time = (double)( GetRunningMicroSecs() - stats_startReloadTime) / 1e6;
|
||||
|
||||
aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::addTopAndBottomTriangles( TRIANGLE_DISPLAY_LIST* aDst, const SFVEC2F& v0,
|
||||
const SFVEC2F& v1, const SFVEC2F& v2, float top,
|
||||
float bot )
|
||||
{
|
||||
aDst->m_layer_bot_triangles->AddTriangle( SFVEC3F( v0.x, v0.y, bot ),
|
||||
SFVEC3F( v1.x, v1.y, bot ),
|
||||
SFVEC3F( v2.x, v2.y, bot ) );
|
||||
|
||||
aDst->m_layer_top_triangles->AddTriangle( SFVEC3F( v2.x, v2.y, top ),
|
||||
SFVEC3F( v1.x, v1.y, top ),
|
||||
SFVEC3F( v0.x, v0.y, top ) );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::getLayerZPos( PCB_LAYER_ID aLayer, float& aOutZtop, float& aOutZbot ) const
|
||||
{
|
||||
aOutZbot = m_boardAdapter.GetLayerBottomZPos( aLayer );
|
||||
aOutZtop = m_boardAdapter.GetLayerTopZPos( aLayer );
|
||||
|
||||
if( aOutZtop < aOutZbot )
|
||||
{
|
||||
float tmpFloat = aOutZbot;
|
||||
aOutZbot = aOutZtop;
|
||||
aOutZtop = tmpFloat;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::generateCylinder( const SFVEC2F& aCenter, float aInnerRadius,
|
||||
float aOuterRadius, float aZtop, float aZbot,
|
||||
unsigned int aNr_sides_per_circle,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer )
|
||||
{
|
||||
std::vector< SFVEC2F > innerContour;
|
||||
std::vector< SFVEC2F > outerContour;
|
||||
|
||||
generateRing( aCenter, aInnerRadius, aOuterRadius, aNr_sides_per_circle, innerContour,
|
||||
outerContour, false );
|
||||
|
||||
for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
|
||||
{
|
||||
const SFVEC2F& vi0 = innerContour[i + 0];
|
||||
const SFVEC2F& vi1 = innerContour[i + 1];
|
||||
const SFVEC2F& vo0 = outerContour[i + 0];
|
||||
const SFVEC2F& vo1 = outerContour[i + 1];
|
||||
|
||||
aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
|
||||
SFVEC3F( vi0.x, vi0.y, aZtop ),
|
||||
SFVEC3F( vo0.x, vo0.y, aZtop ),
|
||||
SFVEC3F( vo1.x, vo1.y, aZtop ) );
|
||||
|
||||
aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
|
||||
SFVEC3F( vo1.x, vo1.y, aZbot ),
|
||||
SFVEC3F( vo0.x, vo0.y, aZbot ),
|
||||
SFVEC3F( vi0.x, vi0.y, aZbot ) );
|
||||
}
|
||||
|
||||
aDstLayer->AddToMiddleContourns( outerContour, aZbot, aZtop, true );
|
||||
aDstLayer->AddToMiddleContourns( innerContour, aZbot, aZtop, false );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::generateViasAndPads()
|
||||
{
|
||||
if( !m_boardAdapter.GetBoard() )
|
||||
return;
|
||||
|
||||
const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
|
||||
const float platingThickness3d = platingThickness * m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
if( m_boardAdapter.GetViaCount() > 0 )
|
||||
{
|
||||
float averageDiameter = m_boardAdapter.GetAverageViaHoleDiameter();
|
||||
unsigned int averageSegCount = m_boardAdapter.GetCircleSegmentCount( averageDiameter );
|
||||
unsigned int trianglesEstimate = averageSegCount * 8 * m_boardAdapter.GetViaCount();
|
||||
|
||||
TRIANGLE_DISPLAY_LIST* layerTriangleVIA = new TRIANGLE_DISPLAY_LIST( trianglesEstimate );
|
||||
|
||||
// Insert plated vertical holes inside the board
|
||||
|
||||
// Insert vias holes (vertical cylinders)
|
||||
for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
{
|
||||
const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
|
||||
|
||||
const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3dUnits();
|
||||
const int nrSegments = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
|
||||
const float hole_inner_radius = holediameter / 2.0f;
|
||||
|
||||
const SFVEC2F via_center( via->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-via->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
|
||||
|
||||
PCB_LAYER_ID top_layer, bottom_layer;
|
||||
via->LayerPair( &top_layer, &bottom_layer );
|
||||
|
||||
float ztop, zbot, dummy;
|
||||
|
||||
getLayerZPos( top_layer, ztop, dummy );
|
||||
getLayerZPos( bottom_layer, dummy, zbot );
|
||||
|
||||
wxASSERT( zbot < ztop );
|
||||
|
||||
generateCylinder( via_center, hole_inner_radius,
|
||||
hole_inner_radius + platingThickness3d,
|
||||
ztop, zbot, nrSegments, layerTriangleVIA );
|
||||
}
|
||||
}
|
||||
|
||||
m_vias = new OPENGL_RENDER_LIST( *layerTriangleVIA, 0, 0.0f, 0.0f );
|
||||
|
||||
delete layerTriangleVIA;
|
||||
}
|
||||
|
||||
|
||||
if( m_boardAdapter.GetHoleCount() > 0 )
|
||||
{
|
||||
SHAPE_POLY_SET tht_outer_holes_poly; // Stores the outer poly of the copper holes (the pad)
|
||||
SHAPE_POLY_SET tht_inner_holes_poly; // Stores the inner poly of the copper holes (the hole)
|
||||
|
||||
tht_outer_holes_poly.RemoveAllContours();
|
||||
tht_inner_holes_poly.RemoveAllContours();
|
||||
|
||||
// Insert pads holes (vertical cylinders)
|
||||
for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
|
||||
{
|
||||
for( const PAD* pad : footprint->Pads() )
|
||||
{
|
||||
if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
|
||||
{
|
||||
const VECTOR2I drillsize = pad->GetDrillSize();
|
||||
const bool hasHole = drillsize.x && drillsize.y;
|
||||
|
||||
if( !hasHole )
|
||||
continue;
|
||||
|
||||
pad->TransformHoleToPolygon( tht_outer_holes_poly, platingThickness,
|
||||
ARC_HIGH_DEF, ERROR_INSIDE );
|
||||
pad->TransformHoleToPolygon( tht_inner_holes_poly, 0, ARC_HIGH_DEF,
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract the holes
|
||||
tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly, SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
if( m_boardAdapter.m_Cfg->m_Render.realistic )
|
||||
tht_outer_holes_poly.BooleanSubtract( m_antiBoardPolys, SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
CONTAINER_2D holesContainer;
|
||||
|
||||
ConvertPolygonToTriangles( tht_outer_holes_poly, holesContainer,
|
||||
m_boardAdapter.BiuTo3dUnits(), *m_boardAdapter.GetBoard() );
|
||||
|
||||
const LIST_OBJECT2D& holes2D = holesContainer.GetList();
|
||||
|
||||
if( holes2D.size() > 0 )
|
||||
{
|
||||
float layer_z_top, layer_z_bot, dummy;
|
||||
|
||||
getLayerZPos( F_Cu, layer_z_top, dummy );
|
||||
getLayerZPos( B_Cu, dummy, layer_z_bot );
|
||||
|
||||
TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( holes2D.size() );
|
||||
|
||||
// Convert the list of objects(triangles) to triangle layer structure
|
||||
for( const OBJECT_2D* itemOnLayer : holes2D )
|
||||
{
|
||||
const OBJECT_2D* object2d_A = itemOnLayer;
|
||||
|
||||
wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
|
||||
|
||||
const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
|
||||
|
||||
const SFVEC2F& v1 = tri->GetP1();
|
||||
const SFVEC2F& v2 = tri->GetP2();
|
||||
const SFVEC2F& v3 = tri->GetP3();
|
||||
|
||||
addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
|
||||
}
|
||||
|
||||
wxASSERT( tht_outer_holes_poly.OutlineCount() > 0 );
|
||||
|
||||
if( tht_outer_holes_poly.OutlineCount() > 0 )
|
||||
{
|
||||
layerTriangles->AddToMiddleContourns( tht_outer_holes_poly,
|
||||
layer_z_bot, layer_z_top,
|
||||
m_boardAdapter.BiuTo3dUnits(), false );
|
||||
|
||||
m_padHoles = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
|
||||
layer_z_top, layer_z_top );
|
||||
}
|
||||
|
||||
delete layerTriangles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::Load3dModelsIfNeeded()
|
||||
{
|
||||
if( m_3dModelMap.size() > 0 )
|
||||
return;
|
||||
|
||||
wxFrame* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( m_canvas->GetParent() );
|
||||
|
||||
if( frame )
|
||||
{
|
||||
STATUSBAR_REPORTER activityReporter( frame->GetStatusBar(),
|
||||
(int) EDA_3D_VIEWER_STATUSBAR::ACTIVITY );
|
||||
load3dModels( &activityReporter );
|
||||
}
|
||||
else
|
||||
load3dModels( nullptr );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter )
|
||||
{
|
||||
if( !m_boardAdapter.GetBoard() )
|
||||
return;
|
||||
|
||||
if( !m_boardAdapter.m_Cfg->m_Render.show_footprints_normal
|
||||
&& !m_boardAdapter.m_Cfg->m_Render.show_footprints_insert
|
||||
&& !m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Go for all footprints
|
||||
for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
|
||||
{
|
||||
wxString libraryName = footprint->GetFPID().GetLibNickname();
|
||||
wxString footprintBasePath = wxEmptyString;
|
||||
|
||||
if( m_boardAdapter.GetBoard()->GetProject() )
|
||||
{
|
||||
try
|
||||
{
|
||||
// FindRow() can throw an exception
|
||||
const FP_LIB_TABLE_ROW* fpRow =
|
||||
m_boardAdapter.GetBoard()->GetProject()->PcbFootprintLibs()->FindRow(
|
||||
libraryName, false );
|
||||
|
||||
if( fpRow )
|
||||
footprintBasePath = fpRow->GetFullURI( true );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
// Do nothing if the libraryName is not found in lib table
|
||||
}
|
||||
}
|
||||
|
||||
for( const FP_3DMODEL& fp_model : footprint->Models() )
|
||||
{
|
||||
if( fp_model.m_Show && !fp_model.m_Filename.empty() )
|
||||
{
|
||||
if( aStatusReporter )
|
||||
{
|
||||
// Display the short filename of the 3D fp_model loaded:
|
||||
// (the full name is usually too long to be displayed)
|
||||
wxFileName fn( fp_model.m_Filename );
|
||||
aStatusReporter->Report( wxString::Format( _( "Loading %s..." ),
|
||||
fn.GetFullName() ) );
|
||||
}
|
||||
|
||||
// Check if the fp_model is not present in our cache map
|
||||
// (Not already loaded in memory)
|
||||
if( m_3dModelMap.find( fp_model.m_Filename ) == m_3dModelMap.end() )
|
||||
{
|
||||
// It is not present, try get it from cache
|
||||
const S3DMODEL* modelPtr =
|
||||
m_boardAdapter.Get3dCacheManager()->GetModel( fp_model.m_Filename, footprintBasePath );
|
||||
|
||||
// only add it if the return is not NULL
|
||||
if( modelPtr )
|
||||
{
|
||||
MATERIAL_MODE materialMode = m_boardAdapter.m_Cfg->m_Render.material_mode;
|
||||
MODEL_3D* model = new MODEL_3D( *modelPtr, materialMode );
|
||||
|
||||
m_3dModelMap[ fp_model.m_Filename ] = model;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,790 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "layer_triangles.h"
|
||||
#include "../raytracing/ray.h"
|
||||
#include <wx/debug.h> // For the wxASSERT
|
||||
#include <mutex>
|
||||
|
||||
|
||||
TRIANGLE_LIST::TRIANGLE_LIST( unsigned int aNrReservedTriangles, bool aReserveNormals )
|
||||
{
|
||||
wxASSERT( aNrReservedTriangles > 0 );
|
||||
|
||||
m_vertexs.clear();
|
||||
m_normals.clear();
|
||||
|
||||
m_vertexs.reserve( aNrReservedTriangles * 3 );
|
||||
|
||||
if( aReserveNormals )
|
||||
m_normals.reserve( aNrReservedTriangles * 3 );
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_LIST::Reserve_More( unsigned int aNrReservedTriangles, bool aReserveNormals )
|
||||
{
|
||||
m_vertexs.reserve( m_vertexs.size() + aNrReservedTriangles * 3 );
|
||||
|
||||
if( aReserveNormals )
|
||||
m_normals.reserve( m_normals.size() + aNrReservedTriangles * 3 );
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_LIST::AddQuad( const SFVEC3F& aV1, const SFVEC3F& aV2, const SFVEC3F& aV3,
|
||||
const SFVEC3F& aV4 )
|
||||
{
|
||||
m_vertexs.push_back( aV1 );
|
||||
m_vertexs.push_back( aV2 );
|
||||
m_vertexs.push_back( aV3 );
|
||||
|
||||
m_vertexs.push_back( aV3 );
|
||||
m_vertexs.push_back( aV4 );
|
||||
m_vertexs.push_back( aV1 );
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_LIST::AddTriangle( const SFVEC3F& aV1, const SFVEC3F& aV2, const SFVEC3F& aV3 )
|
||||
{
|
||||
m_vertexs.push_back( aV1 );
|
||||
m_vertexs.push_back( aV2 );
|
||||
m_vertexs.push_back( aV3 );
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_LIST::AddNormal( const SFVEC3F& aN1, const SFVEC3F& aN2, const SFVEC3F& aN3 )
|
||||
{
|
||||
m_normals.push_back( aN1 );
|
||||
m_normals.push_back( aN2 );
|
||||
m_normals.push_back( aN3 );
|
||||
}
|
||||
|
||||
void TRIANGLE_LIST::AddNormal( const SFVEC3F& aN1, const SFVEC3F& aN2, const SFVEC3F& aN3,
|
||||
const SFVEC3F& aN4 )
|
||||
{
|
||||
m_normals.push_back( aN1 );
|
||||
m_normals.push_back( aN2 );
|
||||
m_normals.push_back( aN3 );
|
||||
|
||||
m_normals.push_back( aN3 );
|
||||
m_normals.push_back( aN4 );
|
||||
m_normals.push_back( aN1 );
|
||||
}
|
||||
|
||||
|
||||
TRIANGLE_DISPLAY_LIST::TRIANGLE_DISPLAY_LIST( unsigned int aNrReservedTriangles )
|
||||
{
|
||||
wxASSERT( aNrReservedTriangles > 0 );
|
||||
|
||||
m_layer_top_segment_ends = new TRIANGLE_LIST( aNrReservedTriangles, false );
|
||||
m_layer_top_triangles = new TRIANGLE_LIST( aNrReservedTriangles, false );
|
||||
m_layer_middle_contourns_quads = new TRIANGLE_LIST( aNrReservedTriangles, true );
|
||||
m_layer_bot_triangles = new TRIANGLE_LIST( aNrReservedTriangles, false );
|
||||
m_layer_bot_segment_ends = new TRIANGLE_LIST( aNrReservedTriangles, false );
|
||||
}
|
||||
|
||||
|
||||
TRIANGLE_DISPLAY_LIST::~TRIANGLE_DISPLAY_LIST()
|
||||
{
|
||||
delete m_layer_top_segment_ends;
|
||||
m_layer_top_segment_ends = nullptr;
|
||||
|
||||
delete m_layer_top_triangles;
|
||||
m_layer_top_triangles = nullptr;
|
||||
|
||||
delete m_layer_middle_contourns_quads;
|
||||
m_layer_middle_contourns_quads = nullptr;
|
||||
|
||||
delete m_layer_bot_triangles;
|
||||
m_layer_bot_triangles = nullptr;
|
||||
|
||||
delete m_layer_bot_segment_ends;
|
||||
m_layer_bot_segment_ends = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_DISPLAY_LIST::AddToMiddleContourns( const std::vector< SFVEC2F >& aContournPoints,
|
||||
float zBot, float zTop, bool aInvertFaceDirection,
|
||||
const BVH_CONTAINER_2D* aThroughHoles )
|
||||
{
|
||||
if( aContournPoints.size() >= 4 )
|
||||
{
|
||||
// Calculate normals of each segment of the contourn
|
||||
std::vector< SFVEC2F > contournNormals;
|
||||
|
||||
contournNormals.clear();
|
||||
contournNormals.resize( aContournPoints.size() - 1 );
|
||||
|
||||
if( aInvertFaceDirection )
|
||||
{
|
||||
for( unsigned int i = 0; i < ( aContournPoints.size() - 1 ); ++i )
|
||||
{
|
||||
const SFVEC2F& v0 = aContournPoints[i + 0];
|
||||
const SFVEC2F& v1 = aContournPoints[i + 1];
|
||||
const SFVEC2F n = glm::normalize( v1 - v0 );
|
||||
|
||||
contournNormals[i] = SFVEC2F( n.y,-n.x );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( unsigned int i = 0; i < ( aContournPoints.size() - 1 ); ++i )
|
||||
{
|
||||
const SFVEC2F& v0 = aContournPoints[i + 0];
|
||||
const SFVEC2F& v1 = aContournPoints[i + 1];
|
||||
const SFVEC2F n = glm::normalize( v1 - v0 );
|
||||
|
||||
contournNormals[i] = SFVEC2F( -n.y, n.x );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( aInvertFaceDirection )
|
||||
std::swap( zBot, zTop );
|
||||
|
||||
const unsigned int nContournsToProcess = ( aContournPoints.size() - 1 );
|
||||
|
||||
for( unsigned int i = 0; i < nContournsToProcess; ++i )
|
||||
{
|
||||
SFVEC2F lastNormal;
|
||||
|
||||
if( i > 0 )
|
||||
lastNormal = contournNormals[i - 1];
|
||||
else
|
||||
lastNormal = contournNormals[nContournsToProcess - 1];
|
||||
|
||||
SFVEC2F n0 = contournNormals[i];
|
||||
|
||||
// Only interpolate the normal if the angle is closer
|
||||
if( glm::dot( n0, lastNormal ) > 0.5f )
|
||||
n0 = glm::normalize( n0 + lastNormal );
|
||||
|
||||
SFVEC2F nextNormal;
|
||||
|
||||
if( i < (nContournsToProcess - 1) )
|
||||
nextNormal = contournNormals[i + 1];
|
||||
else
|
||||
nextNormal = contournNormals[0];
|
||||
|
||||
SFVEC2F n1 = contournNormals[i];
|
||||
|
||||
if( glm::dot( n1, nextNormal ) > 0.5f )
|
||||
n1 = glm::normalize( n1 + nextNormal );
|
||||
|
||||
const SFVEC3F n3d0 = SFVEC3F( n0.x, n0.y, 0.0f );
|
||||
const SFVEC3F n3d1 = SFVEC3F( n1.x, n1.y, 0.0f );
|
||||
|
||||
const SFVEC2F& v0 = aContournPoints[i + 0];
|
||||
const SFVEC2F& v1 = aContournPoints[i + 1];
|
||||
|
||||
if( aThroughHoles && aThroughHoles->IntersectAny( RAYSEG2D( v0, v1 ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( m_middle_layer_lock );
|
||||
m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, zTop ),
|
||||
SFVEC3F( v1.x, v1.y, zTop ),
|
||||
SFVEC3F( v1.x, v1.y, zBot ),
|
||||
SFVEC3F( v0.x, v0.y, zBot ) );
|
||||
|
||||
m_layer_middle_contourns_quads->AddNormal( n3d0, n3d1, n3d1, n3d0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_DISPLAY_LIST::AddToMiddleContourns( const SHAPE_LINE_CHAIN& outlinePath, float zBot,
|
||||
float zTop, double aBiuTo3Du,
|
||||
bool aInvertFaceDirection,
|
||||
const BVH_CONTAINER_2D* aThroughHoles )
|
||||
{
|
||||
std::vector< SFVEC2F >contournPoints;
|
||||
|
||||
contournPoints.clear();
|
||||
contournPoints.reserve( outlinePath.PointCount() + 2 );
|
||||
|
||||
const VECTOR2I& firstV = outlinePath.CPoint( 0 );
|
||||
|
||||
SFVEC2F lastV = SFVEC2F( firstV.x * aBiuTo3Du, -firstV.y * aBiuTo3Du );
|
||||
|
||||
contournPoints.push_back( lastV );
|
||||
|
||||
for( unsigned int i = 1; i < (unsigned int)outlinePath.PointCount(); ++i )
|
||||
{
|
||||
const VECTOR2I& v = outlinePath.CPoint( i );
|
||||
|
||||
const SFVEC2F vf = SFVEC2F( v.x * aBiuTo3Du, -v.y * aBiuTo3Du );
|
||||
|
||||
if( vf != lastV ) // Do not add repeated points
|
||||
{
|
||||
lastV = vf;
|
||||
contournPoints.push_back( vf );
|
||||
}
|
||||
}
|
||||
|
||||
// Add first position fo the list to close the path
|
||||
if( lastV != contournPoints[0] )
|
||||
contournPoints.push_back( contournPoints[0] );
|
||||
|
||||
AddToMiddleContourns( contournPoints, zBot, zTop, aInvertFaceDirection, aThroughHoles );
|
||||
}
|
||||
|
||||
|
||||
void TRIANGLE_DISPLAY_LIST::AddToMiddleContourns( const SHAPE_POLY_SET& aPolySet, float zBot,
|
||||
float zTop, double aBiuTo3Du,
|
||||
bool aInvertFaceDirection,
|
||||
const BVH_CONTAINER_2D* aThroughHoles )
|
||||
{
|
||||
if( aPolySet.OutlineCount() == 0 )
|
||||
return;
|
||||
|
||||
// Calculate an estimation of points to reserve
|
||||
unsigned int nrContournPointsToReserve = 0;
|
||||
|
||||
for( int i = 0; i < aPolySet.OutlineCount(); ++i )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i );
|
||||
|
||||
nrContournPointsToReserve += pathOutline.PointCount();
|
||||
|
||||
for( int h = 0; h < aPolySet.HoleCount( i ); ++h )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& hole = aPolySet.CHole( i, h );
|
||||
|
||||
nrContournPointsToReserve += hole.PointCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Request to reserve more space
|
||||
m_layer_middle_contourns_quads->Reserve_More( nrContournPointsToReserve * 2, true );
|
||||
|
||||
for( int i = 0; i < aPolySet.OutlineCount(); i++ )
|
||||
{
|
||||
// Add outline
|
||||
const SHAPE_LINE_CHAIN& pathOutline = aPolySet.COutline( i );
|
||||
|
||||
AddToMiddleContourns( pathOutline, zBot, zTop, aBiuTo3Du, aInvertFaceDirection,
|
||||
aThroughHoles );
|
||||
|
||||
// Add holes for this outline
|
||||
for( int h = 0; h < aPolySet.HoleCount( i ); ++h )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& hole = aPolySet.CHole( i, h );
|
||||
AddToMiddleContourns( hole, zBot, zTop, aBiuTo3Du, aInvertFaceDirection,
|
||||
aThroughHoles );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OPENGL_RENDER_LIST::OPENGL_RENDER_LIST( const TRIANGLE_DISPLAY_LIST& aLayerTriangles,
|
||||
GLuint aTextureIndexForSegEnds,
|
||||
float aZBot, float aZTop )
|
||||
{
|
||||
m_zBot = aZBot;
|
||||
m_zTop = aZTop;
|
||||
|
||||
m_layer_top_segment_ends = 0;
|
||||
m_layer_top_triangles = 0;
|
||||
m_layer_middle_contourns_quads = 0;
|
||||
m_layer_bot_triangles = 0;
|
||||
m_layer_bot_segment_ends = 0;
|
||||
|
||||
if( aTextureIndexForSegEnds )
|
||||
{
|
||||
wxASSERT( glIsTexture( aTextureIndexForSegEnds ) );
|
||||
|
||||
if( glIsTexture( aTextureIndexForSegEnds ) )
|
||||
{
|
||||
m_layer_top_segment_ends =
|
||||
generate_top_or_bot_seg_ends( aLayerTriangles.m_layer_top_segment_ends,
|
||||
true, aTextureIndexForSegEnds );
|
||||
|
||||
m_layer_bot_segment_ends =
|
||||
generate_top_or_bot_seg_ends( aLayerTriangles.m_layer_bot_segment_ends,
|
||||
false, aTextureIndexForSegEnds );
|
||||
}
|
||||
}
|
||||
|
||||
m_layer_top_triangles = generate_top_or_bot_triangles( aLayerTriangles.m_layer_top_triangles,
|
||||
true );
|
||||
|
||||
m_layer_bot_triangles = generate_top_or_bot_triangles( aLayerTriangles.m_layer_bot_triangles,
|
||||
false );
|
||||
|
||||
|
||||
if( aLayerTriangles.m_layer_middle_contourns_quads->GetVertexSize() > 0 )
|
||||
{
|
||||
m_layer_middle_contourns_quads =
|
||||
generate_middle_triangles( aLayerTriangles.m_layer_middle_contourns_quads );
|
||||
}
|
||||
|
||||
m_draw_it_transparent = false;
|
||||
m_haveTransformation = false;
|
||||
m_zPositionTransformation = 0.0f;
|
||||
m_zScaleTransformation = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
OPENGL_RENDER_LIST::~OPENGL_RENDER_LIST()
|
||||
{
|
||||
if( glIsList( m_layer_top_segment_ends ) )
|
||||
glDeleteLists( m_layer_top_segment_ends, 1 );
|
||||
|
||||
if( glIsList( m_layer_top_triangles ) )
|
||||
glDeleteLists( m_layer_top_triangles, 1 );
|
||||
|
||||
if( glIsList( m_layer_middle_contourns_quads ) )
|
||||
glDeleteLists( m_layer_middle_contourns_quads, 1 );
|
||||
|
||||
if( glIsList( m_layer_bot_triangles ) )
|
||||
glDeleteLists( m_layer_bot_triangles, 1 );
|
||||
|
||||
if( glIsList( m_layer_bot_segment_ends ) )
|
||||
glDeleteLists( m_layer_bot_segment_ends, 1 );
|
||||
|
||||
m_layer_top_segment_ends = 0;
|
||||
m_layer_top_triangles = 0;
|
||||
m_layer_middle_contourns_quads = 0;
|
||||
m_layer_bot_triangles = 0;
|
||||
m_layer_bot_segment_ends = 0;
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawTopAndMiddle() const
|
||||
{
|
||||
beginTransformation();
|
||||
|
||||
if( glIsList( m_layer_middle_contourns_quads ) )
|
||||
glCallList( m_layer_middle_contourns_quads );
|
||||
|
||||
if( glIsList( m_layer_top_triangles ) )
|
||||
glCallList( m_layer_top_triangles );
|
||||
|
||||
if( glIsList( m_layer_top_segment_ends ) )
|
||||
glCallList( m_layer_top_segment_ends );
|
||||
|
||||
endTransformation();
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawBotAndMiddle() const
|
||||
{
|
||||
beginTransformation();
|
||||
|
||||
if( glIsList( m_layer_middle_contourns_quads ) )
|
||||
glCallList( m_layer_middle_contourns_quads );
|
||||
|
||||
if( glIsList( m_layer_bot_triangles ) )
|
||||
glCallList( m_layer_bot_triangles );
|
||||
|
||||
if( glIsList( m_layer_bot_segment_ends ) )
|
||||
glCallList( m_layer_bot_segment_ends );
|
||||
|
||||
endTransformation();
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawTop() const
|
||||
{
|
||||
beginTransformation();
|
||||
|
||||
if( glIsList( m_layer_top_triangles ) )
|
||||
glCallList( m_layer_top_triangles );
|
||||
|
||||
if( glIsList( m_layer_top_segment_ends ) )
|
||||
glCallList( m_layer_top_segment_ends );
|
||||
|
||||
endTransformation();
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawBot() const
|
||||
{
|
||||
beginTransformation();
|
||||
|
||||
if( glIsList( m_layer_bot_triangles ) )
|
||||
glCallList( m_layer_bot_triangles );
|
||||
|
||||
if( glIsList( m_layer_bot_segment_ends ) )
|
||||
glCallList( m_layer_bot_segment_ends );
|
||||
|
||||
endTransformation();
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawMiddle() const
|
||||
{
|
||||
beginTransformation();
|
||||
|
||||
if( glIsList( m_layer_middle_contourns_quads ) )
|
||||
glCallList( m_layer_middle_contourns_quads );
|
||||
|
||||
endTransformation();
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawAll( bool aDrawMiddle ) const
|
||||
{
|
||||
beginTransformation();
|
||||
|
||||
if( aDrawMiddle )
|
||||
if( glIsList( m_layer_middle_contourns_quads ) )
|
||||
glCallList( m_layer_middle_contourns_quads );
|
||||
|
||||
if( glIsList( m_layer_top_triangles ) )
|
||||
glCallList( m_layer_top_triangles );
|
||||
|
||||
if( glIsList( m_layer_bot_triangles ) )
|
||||
glCallList( m_layer_bot_triangles );
|
||||
|
||||
if( glIsList( m_layer_top_segment_ends ) )
|
||||
glCallList( m_layer_top_segment_ends );
|
||||
|
||||
if( glIsList( m_layer_bot_segment_ends ) )
|
||||
glCallList( m_layer_bot_segment_ends );
|
||||
|
||||
endTransformation();
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawAllCameraCulled( float zCameraPos, bool aDrawMiddle ) const
|
||||
{
|
||||
zCameraPos = m_haveTransformation
|
||||
? ( ( zCameraPos - m_zPositionTransformation ) / m_zScaleTransformation )
|
||||
: zCameraPos;
|
||||
|
||||
if( aDrawMiddle )
|
||||
DrawMiddle();
|
||||
|
||||
if( zCameraPos > m_zTop )
|
||||
{
|
||||
DrawTop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( zCameraPos < m_zBot )
|
||||
{
|
||||
DrawBot();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If camera is in the middle dont draw it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::DrawAllCameraCulledSubtractLayer( bool aDrawMiddle,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractA,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractB,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractC,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractD ) const
|
||||
{
|
||||
glClearStencil( 0x00 );
|
||||
glClear( GL_STENCIL_BUFFER_BIT );
|
||||
|
||||
glEnable( GL_CULL_FACE );
|
||||
glCullFace( GL_BACK );
|
||||
|
||||
glDisable( GL_DEPTH_TEST );
|
||||
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
glDepthMask( GL_FALSE );
|
||||
glEnable( GL_STENCIL_TEST );
|
||||
glStencilFunc( GL_ALWAYS, 1, 0 );
|
||||
glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
|
||||
|
||||
if( aLayerToSubtractA )
|
||||
aLayerToSubtractA->DrawBot();
|
||||
|
||||
if( aLayerToSubtractB )
|
||||
aLayerToSubtractB->DrawBot();
|
||||
|
||||
if( aLayerToSubtractC )
|
||||
aLayerToSubtractC->DrawBot();
|
||||
|
||||
if( aLayerToSubtractD )
|
||||
aLayerToSubtractD->DrawBot();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
|
||||
glStencilFunc( GL_EQUAL, 0, 1 );
|
||||
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
|
||||
DrawBot();
|
||||
|
||||
glDisable( GL_DEPTH_TEST );
|
||||
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
glDepthMask( GL_FALSE );
|
||||
glEnable( GL_STENCIL_TEST );
|
||||
glStencilFunc( GL_ALWAYS, 2, 0 );
|
||||
glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
|
||||
|
||||
if( aLayerToSubtractA )
|
||||
aLayerToSubtractA->DrawTop();
|
||||
|
||||
if( aLayerToSubtractB )
|
||||
aLayerToSubtractB->DrawTop();
|
||||
|
||||
if( aLayerToSubtractC )
|
||||
aLayerToSubtractC->DrawTop();
|
||||
|
||||
if( aLayerToSubtractD )
|
||||
aLayerToSubtractD->DrawTop();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
|
||||
glStencilFunc( GL_NOTEQUAL, 2, 0x03 );
|
||||
glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
|
||||
DrawTop();
|
||||
|
||||
if( aDrawMiddle )
|
||||
DrawMiddle();
|
||||
|
||||
glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );
|
||||
|
||||
glCullFace( GL_FRONT );
|
||||
glStencilFunc( GL_GEQUAL, 3, 0x03 );
|
||||
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
|
||||
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
|
||||
|
||||
if( aDrawMiddle )
|
||||
{
|
||||
if( aLayerToSubtractA )
|
||||
aLayerToSubtractA->DrawMiddle();
|
||||
}
|
||||
|
||||
glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
|
||||
|
||||
glCullFace( GL_BACK );
|
||||
glDisable( GL_STENCIL_TEST );
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::ApplyScalePosition( float aZposition, float aZscale )
|
||||
{
|
||||
wxASSERT( aZscale > FLT_EPSILON );
|
||||
|
||||
m_zPositionTransformation = aZposition;
|
||||
m_zScaleTransformation = aZscale;
|
||||
m_haveTransformation = true;
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::SetItIsTransparent( bool aSetTransparent )
|
||||
{
|
||||
m_draw_it_transparent = aSetTransparent;
|
||||
}
|
||||
|
||||
|
||||
GLuint OPENGL_RENDER_LIST::generate_top_or_bot_seg_ends(
|
||||
const TRIANGLE_LIST* aTriangleContainer, bool aIsNormalUp, GLuint aTextureId ) const
|
||||
{
|
||||
wxASSERT( aTriangleContainer != nullptr );
|
||||
|
||||
wxASSERT( ( aTriangleContainer->GetVertexSize() % 3 ) == 0 );
|
||||
|
||||
// Top and Bot dont have normals array stored in container
|
||||
wxASSERT( aTriangleContainer->GetNormalsSize() == 0 );
|
||||
|
||||
if( ( aTriangleContainer->GetVertexSize() > 0 )
|
||||
&& ( ( aTriangleContainer->GetVertexSize() % 3 ) == 0 ) )
|
||||
{
|
||||
GLuint listIdx = glGenLists( 1 );
|
||||
|
||||
if( glIsList( listIdx ) )
|
||||
{
|
||||
// Prepare an array of UV text coordinates
|
||||
SFVEC2F* uvArray = new SFVEC2F[aTriangleContainer->GetVertexSize()];
|
||||
|
||||
for( unsigned int i = 0; i < aTriangleContainer->GetVertexSize(); i += 3 )
|
||||
{
|
||||
uvArray[i + 0] = SFVEC2F( 1.0f, 0.0f );
|
||||
uvArray[i + 1] = SFVEC2F( 0.0f, 1.0f );
|
||||
uvArray[i + 2] = SFVEC2F( 0.0f, 0.0f );
|
||||
}
|
||||
|
||||
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glDisableClientState( GL_COLOR_ARRAY );
|
||||
glDisableClientState( GL_NORMAL_ARRAY );
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glVertexPointer( 3, GL_FLOAT, 0, aTriangleContainer->GetVertexPointer() );
|
||||
glTexCoordPointer( 2, GL_FLOAT, 0, uvArray );
|
||||
|
||||
glNewList( listIdx, GL_COMPILE );
|
||||
|
||||
glDisable( GL_COLOR_MATERIAL );
|
||||
|
||||
glEnable( GL_TEXTURE_2D );
|
||||
glBindTexture( GL_TEXTURE_2D, aTextureId );
|
||||
|
||||
glAlphaFunc( GL_GREATER, 0.2f );
|
||||
glEnable( GL_ALPHA_TEST );
|
||||
|
||||
glNormal3f( 0.0f, 0.0f, aIsNormalUp?1.0f:-1.0f );
|
||||
|
||||
glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() );
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, 0 );
|
||||
glDisable( GL_TEXTURE_2D );
|
||||
glDisable( GL_ALPHA_TEST );
|
||||
glDisable( GL_BLEND );
|
||||
|
||||
glEndList();
|
||||
|
||||
glDisableClientState( GL_VERTEX_ARRAY );
|
||||
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
delete [] uvArray;
|
||||
return listIdx;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
GLuint OPENGL_RENDER_LIST::generate_top_or_bot_triangles( const TRIANGLE_LIST* aTriangleContainer,
|
||||
bool aIsNormalUp ) const
|
||||
{
|
||||
wxASSERT( aTriangleContainer != nullptr );
|
||||
|
||||
wxASSERT( ( aTriangleContainer->GetVertexSize() % 3 ) == 0 );
|
||||
|
||||
// Top and Bot dont have normals array stored in container
|
||||
wxASSERT( aTriangleContainer->GetNormalsSize() == 0 );
|
||||
|
||||
if( ( aTriangleContainer->GetVertexSize() > 0 )
|
||||
&& ( ( aTriangleContainer->GetVertexSize() % 3 ) == 0 ) )
|
||||
{
|
||||
const GLuint listIdx = glGenLists( 1 );
|
||||
|
||||
if( glIsList( listIdx ) )
|
||||
{
|
||||
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glDisableClientState( GL_COLOR_ARRAY );
|
||||
glDisableClientState( GL_NORMAL_ARRAY );
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glVertexPointer( 3, GL_FLOAT, 0, aTriangleContainer->GetVertexPointer() );
|
||||
|
||||
glNewList( listIdx, GL_COMPILE );
|
||||
|
||||
setBlendfunction();
|
||||
|
||||
glNormal3f( 0.0f, 0.0f, aIsNormalUp?1.0f:-1.0f );
|
||||
|
||||
glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() );
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
glEndList();
|
||||
|
||||
glDisableClientState( GL_VERTEX_ARRAY );
|
||||
|
||||
return listIdx;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
GLuint OPENGL_RENDER_LIST::generate_middle_triangles(
|
||||
const TRIANGLE_LIST* aTriangleContainer ) const
|
||||
{
|
||||
wxASSERT( aTriangleContainer != nullptr );
|
||||
|
||||
// We expect that it is a multiple of 3 vertex
|
||||
wxASSERT( ( aTriangleContainer->GetVertexSize() % 3 ) == 0 );
|
||||
|
||||
// We expect that it is a multiple of 6 vertex (because we expect to add quads)
|
||||
wxASSERT( (aTriangleContainer->GetVertexSize() % 6 ) == 0 );
|
||||
|
||||
// We expect that there are normals with same size as vertex
|
||||
wxASSERT( aTriangleContainer->GetNormalsSize() == aTriangleContainer->GetVertexSize() );
|
||||
|
||||
if( ( aTriangleContainer->GetVertexSize() > 0 )
|
||||
&& ( ( aTriangleContainer->GetVertexSize() % 3 ) == 0 )
|
||||
&& ( ( aTriangleContainer->GetVertexSize() % 6 ) == 0 )
|
||||
&& ( aTriangleContainer->GetNormalsSize() == aTriangleContainer->GetVertexSize() ) )
|
||||
{
|
||||
const GLuint listIdx = glGenLists( 1 );
|
||||
|
||||
if( glIsList( listIdx ) )
|
||||
{
|
||||
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
glDisableClientState( GL_COLOR_ARRAY );
|
||||
glEnableClientState( GL_NORMAL_ARRAY );
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
glVertexPointer( 3, GL_FLOAT, 0, aTriangleContainer->GetVertexPointer() );
|
||||
glNormalPointer( GL_FLOAT, 0, aTriangleContainer->GetNormalsPointer() );
|
||||
|
||||
glNewList( listIdx, GL_COMPILE );
|
||||
|
||||
setBlendfunction();
|
||||
|
||||
glDrawArrays( GL_TRIANGLES, 0, aTriangleContainer->GetVertexSize() );
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
glEndList();
|
||||
|
||||
glDisableClientState( GL_VERTEX_ARRAY );
|
||||
glDisableClientState( GL_NORMAL_ARRAY );
|
||||
|
||||
return listIdx;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::endTransformation() const
|
||||
{
|
||||
if( m_haveTransformation )
|
||||
{
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::setBlendfunction() const
|
||||
{
|
||||
glEnable( GL_BLEND );
|
||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_RENDER_LIST::beginTransformation() const
|
||||
{
|
||||
if( m_haveTransformation )
|
||||
{
|
||||
glPushMatrix();
|
||||
glTranslatef( 0.0f, 0.0f, m_zPositionTransformation );
|
||||
glScalef( 1.0f, 1.0f, m_zScaleTransformation );
|
||||
}
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef TRIANGLE_DISPLAY_LIST_H
|
||||
#define TRIANGLE_DISPLAY_LIST_H
|
||||
|
||||
#include "../../common_ogl/openGL_includes.h"
|
||||
#include <plugins/3dapi/xv3d_types.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <raytracing/accelerators/container_2d.h>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
typedef std::vector< SFVEC3F > SFVEC3F_VECTOR;
|
||||
|
||||
|
||||
/**
|
||||
* Container to manage a vector of triangles.
|
||||
*/
|
||||
class TRIANGLE_LIST
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @param aNrReservedTriangles is number of triangles expected to be used.
|
||||
* @param aReserveNormals if you use normals, set it to bool to reserve space.
|
||||
*/
|
||||
TRIANGLE_LIST( unsigned int aNrReservedTriangles, bool aReserveNormals );
|
||||
|
||||
/**
|
||||
* Reserve more triangles.
|
||||
*/
|
||||
void Reserve_More( unsigned int aNrReservedTriangles, bool aReserveNormals );
|
||||
|
||||
void AddTriangle( const SFVEC3F& aV1, const SFVEC3F& aV2, const SFVEC3F& aV3 );
|
||||
|
||||
void AddQuad( const SFVEC3F& aV1, const SFVEC3F& aV2, const SFVEC3F& aV3, const SFVEC3F& aV4 );
|
||||
|
||||
void AddNormal( const SFVEC3F& aN1, const SFVEC3F& aN2, const SFVEC3F& aN3 );
|
||||
|
||||
void AddNormal( const SFVEC3F& aN1, const SFVEC3F& aN2, const SFVEC3F& aN3,
|
||||
const SFVEC3F& aN4 );
|
||||
|
||||
/**
|
||||
* Get the array of vertices.
|
||||
*
|
||||
* @return a pointer to the start of array vertex.
|
||||
*/
|
||||
const float* GetVertexPointer() const { return (const float *)&m_vertexs[0].x; }
|
||||
|
||||
/**
|
||||
* Get the array of normals.
|
||||
*
|
||||
* @return a pointer to start of array of normals.
|
||||
*/
|
||||
const float* GetNormalsPointer() const { return (const float*) &m_normals[0].x; }
|
||||
|
||||
unsigned int GetVertexSize() const { return (unsigned int) m_vertexs.size(); }
|
||||
|
||||
unsigned int GetNormalsSize() const { return (unsigned int) m_normals.size(); }
|
||||
|
||||
private:
|
||||
SFVEC3F_VECTOR m_vertexs; ///< vertex array
|
||||
SFVEC3F_VECTOR m_normals; ///< normals array
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Store arrays of triangles to be used to create display lists.
|
||||
*/
|
||||
class TRIANGLE_DISPLAY_LIST
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initialize arrays with reserved triangles.
|
||||
*
|
||||
* @param aNrReservedTriangles is the number of triangles to reserve.
|
||||
*/
|
||||
explicit TRIANGLE_DISPLAY_LIST( unsigned int aNrReservedTriangles );
|
||||
|
||||
~TRIANGLE_DISPLAY_LIST();
|
||||
|
||||
/**
|
||||
* Check if the vertex arrays of the layers are as expected.
|
||||
*
|
||||
* @return true if layers are correctly setup.
|
||||
*/
|
||||
bool IsLayersSizeValid();
|
||||
|
||||
|
||||
void AddToMiddleContourns( const SHAPE_LINE_CHAIN& outlinePath, float zBot, float zTop,
|
||||
double aBiuTo3Du, bool aInvertFaceDirection,
|
||||
const BVH_CONTAINER_2D* aThroughHoles = nullptr );
|
||||
|
||||
void AddToMiddleContourns( const SHAPE_POLY_SET& aPolySet, float zBot, float zTop,
|
||||
double aBiuTo3Du, bool aInvertFaceDirection,
|
||||
const BVH_CONTAINER_2D* aThroughHoles = nullptr );
|
||||
|
||||
void AddToMiddleContourns( const std::vector< SFVEC2F >& aContournPoints, float zBot,
|
||||
float zTop, bool aInvertFaceDirection,
|
||||
const BVH_CONTAINER_2D* aThroughHoles = nullptr );
|
||||
|
||||
std::mutex m_middle_layer_lock;
|
||||
|
||||
TRIANGLE_LIST* m_layer_top_segment_ends;
|
||||
TRIANGLE_LIST* m_layer_top_triangles;
|
||||
TRIANGLE_LIST* m_layer_middle_contourns_quads;
|
||||
TRIANGLE_LIST* m_layer_bot_triangles;
|
||||
TRIANGLE_LIST* m_layer_bot_segment_ends;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Store the OpenGL display lists to related with a layer.
|
||||
*/
|
||||
class OPENGL_RENDER_LIST
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create the display lists for a layer.
|
||||
*
|
||||
* @param aLayerTriangles contains the layers array of vertex to render to display lists.
|
||||
* @param aTextureIndexForSegEnds is the texture index to be used by segment ends.
|
||||
* It is a black and white squared texture
|
||||
* with a center circle diameter of the size
|
||||
* of the texture.
|
||||
*/
|
||||
OPENGL_RENDER_LIST( const TRIANGLE_DISPLAY_LIST& aLayerTriangles,
|
||||
GLuint aTextureIndexForSegEnds, float aZBot, float aZTop );
|
||||
|
||||
/**
|
||||
* Destroy this class while free the display lists from GPU memory.
|
||||
*/
|
||||
~OPENGL_RENDER_LIST();
|
||||
|
||||
/**
|
||||
* Call the display lists for the top elements and middle contours.
|
||||
*/
|
||||
void DrawTopAndMiddle() const;
|
||||
|
||||
/**
|
||||
* Call the display lists for the bottom elements and middle contours.
|
||||
*/
|
||||
void DrawBotAndMiddle() const;
|
||||
|
||||
/**
|
||||
* Call the display lists for the top elements.
|
||||
*/
|
||||
void DrawTop() const;
|
||||
|
||||
/**
|
||||
* Call the display lists for the bottom elements.
|
||||
*/
|
||||
void DrawBot() const;
|
||||
|
||||
/**
|
||||
* Call the display lists for the middle elements.
|
||||
*/
|
||||
void DrawMiddle() const;
|
||||
|
||||
/**
|
||||
* Call to draw all the display lists.
|
||||
*/
|
||||
void DrawAll( bool aDrawMiddle = true ) const;
|
||||
|
||||
/**
|
||||
* Draw all layers if they are visible by the camera if camera position is above the layer.
|
||||
*
|
||||
* This only works because the board is centered and the planes are always perpendicular to
|
||||
* the Z axis.
|
||||
*
|
||||
* @param zCameraPos is the camera z axis position.
|
||||
*/
|
||||
void DrawAllCameraCulled( float zCameraPos, bool aDrawMiddle = true ) const;
|
||||
|
||||
void DrawAllCameraCulledSubtractLayer( bool aDrawMiddle,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractA = nullptr,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractB = nullptr,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractC = nullptr,
|
||||
const OPENGL_RENDER_LIST* aLayerToSubtractD = nullptr ) const;
|
||||
|
||||
void ApplyScalePosition( float aZposition, float aZscale );
|
||||
|
||||
void ClearScalePosition() { m_haveTransformation = false; }
|
||||
|
||||
void SetItIsTransparent( bool aSetTransparent );
|
||||
|
||||
float GetZBot() const { return m_zBot; }
|
||||
float GetZTop() const { return m_zTop; }
|
||||
|
||||
private:
|
||||
GLuint generate_top_or_bot_seg_ends( const TRIANGLE_LIST* aTriangleContainer,
|
||||
bool aIsNormalUp, GLuint aTextureId ) const;
|
||||
|
||||
GLuint generate_top_or_bot_triangles( const TRIANGLE_LIST* aTriangleContainer,
|
||||
bool aIsNormalUp ) const;
|
||||
|
||||
GLuint generate_middle_triangles( const TRIANGLE_LIST* aTriangleContainer ) const;
|
||||
|
||||
void beginTransformation() const;
|
||||
void endTransformation() const;
|
||||
|
||||
void setBlendfunction() const;
|
||||
|
||||
private:
|
||||
float m_zBot;
|
||||
float m_zTop;
|
||||
GLuint m_layer_top_segment_ends;
|
||||
GLuint m_layer_top_triangles;
|
||||
GLuint m_layer_middle_contourns_quads;
|
||||
GLuint m_layer_bot_triangles;
|
||||
GLuint m_layer_bot_segment_ends;
|
||||
|
||||
bool m_haveTransformation;
|
||||
float m_zPositionTransformation;
|
||||
float m_zScaleTransformation;
|
||||
|
||||
bool m_draw_it_transparent;
|
||||
};
|
||||
|
||||
#endif // TRIANGLE_DISPLAY_LIST_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user