Solid.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * Solid.js
  3. *
  4. * @author realor
  5. */
  6. import { BSP } from './BSP.js'
  7. import { SolidGeometry } from './SolidGeometry.js'
  8. import { SolidOptimizer } from './SolidOptimizer.js'
  9. import { Formula } from '../formula/Formula.js'
  10. import * as THREE from '../lib/three.module.js'
  11. class Solid extends THREE.Object3D {
  12. static EDGES_NAME = 'edges'
  13. static FACES_NAME = 'faces'
  14. /* static FaceMaterial = new THREE.MeshPhongMaterial({
  15. name: 'SolidFaceMaterial',
  16. color: 0xc0c0c0,
  17. side: THREE.DoubleSide,
  18. polygonOffset: true,
  19. polygonOffsetFactor: 1,
  20. shininess: 1,
  21. flatShading: false
  22. }) */
  23. static FaceMaterial = new THREE.MeshBasicMaterial({
  24. name: 'SolidFaceMaterial',
  25. color: 0xc0c0c0,
  26. side: THREE.DoubleSide,
  27. polygonOffset: true,
  28. polygonOffsetFactor: 1,
  29. shininess: 1,
  30. flatShading: false,
  31. })
  32. static EdgeMaterial = new THREE.LineBasicMaterial({
  33. name: 'SolidEdgeMaterial',
  34. color: 0x0,
  35. opacity: 0.4,
  36. transparent: true,
  37. })
  38. constructor(geometry, material) {
  39. super()
  40. this.type = 'Solid'
  41. this._facesObject = new THREE.Mesh(undefined, Solid.FaceMaterial)
  42. this._facesObject.name = THREE.Object3D.HIDDEN_PREFIX + Solid.FACES_NAME
  43. this._facesObject.matrixAutoUpdate = false
  44. this._facesObject.visible = true
  45. this._facesObject.raycast = function () {}
  46. this.add(this._facesObject)
  47. this._edgesObject = new THREE.LineSegments(undefined, Solid.EdgeMaterial)
  48. this._edgesObject.name = THREE.Object3D.HIDDEN_PREFIX + Solid.EDGES_NAME
  49. this._edgesObject.matrixAutoUpdate = false
  50. this._edgesObject.visible = true
  51. this._edgesObject.raycast = function () {}
  52. this.add(this._edgesObject)
  53. this.builder = null
  54. this.castShadow = true
  55. this.receiveShadow = true
  56. if (geometry) {
  57. this.geometry = geometry
  58. }
  59. if (material) {
  60. this.material = material
  61. }
  62. }
  63. get facesObject() {
  64. return this._facesObject
  65. }
  66. get edgesObject() {
  67. return this._edgesObject
  68. }
  69. get geometry() {
  70. return this._facesObject.geometry
  71. }
  72. set geometry(geometry) {
  73. this.updateGeometry(geometry)
  74. }
  75. get edgesGeometry() {
  76. return this._edgesObject.geometry
  77. }
  78. get facesVisible() {
  79. return this._facesObject.visible
  80. }
  81. set facesVisible(facesVisible) {
  82. this._facesObject.visible = facesVisible
  83. }
  84. get edgesVisible() {
  85. return this._edgesObject.visible
  86. }
  87. set edgesVisible(edgesVisible) {
  88. this._edgesObject.visible = edgesVisible
  89. }
  90. get castShadow() {
  91. return this._facesObject ? this._facesObject.castShadow : false
  92. }
  93. set castShadow(shadow) {
  94. if (this._facesObject) {
  95. this._facesObject.castShadow = shadow
  96. }
  97. }
  98. get receiveShadow() {
  99. return this._facesObject ? this._facesObject.receiveShadow : false
  100. }
  101. set receiveShadow(shadow) {
  102. if (this._facesObject) {
  103. this._facesObject.receiveShadow = shadow
  104. }
  105. }
  106. get material() {
  107. return this.faceMaterial
  108. }
  109. set material(material) {
  110. this.faceMaterial = material
  111. }
  112. get faceMaterial() {
  113. return this._facesObject.material
  114. }
  115. set faceMaterial(material) {
  116. if (material instanceof THREE.Material) {
  117. if (this._facesObject.material !== material) {
  118. this._facesObject.material.dispose()
  119. this._facesObject.material = material
  120. }
  121. }
  122. }
  123. get edgeMaterial() {
  124. return this._edgesObject.material
  125. }
  126. set edgeMaterial(material) {
  127. if (material instanceof THREE.Material) {
  128. if (this._edgesObject.material !== material) {
  129. this._edgesObject.material.dispose()
  130. this._edgesObject.material = material
  131. }
  132. }
  133. }
  134. union(solids) {
  135. return this.booleanOperation('union', solids)
  136. }
  137. intersect(solids) {
  138. return this.booleanOperation('intersect', solids)
  139. }
  140. subtract(solids) {
  141. return this.booleanOperation('subtract', solids)
  142. }
  143. isValid() {
  144. return this._facesObject.geometry.faces.length > 3
  145. }
  146. booleanOperation(oper, solids, newSolid) {
  147. if (!this.isValid()) return this
  148. const createBSP = function (solid) {
  149. const bsp = new BSP()
  150. const geometry = solid.geometry
  151. const matrixWorld = solid.matrixWorld
  152. bsp.fromSolidGeometry(geometry, matrixWorld)
  153. return bsp
  154. }
  155. if (solids instanceof Solid) {
  156. solids = [solids]
  157. }
  158. let resultBSP = createBSP(this)
  159. for (let i = 0; i < solids.length; i++) {
  160. let solid = solids[i]
  161. if (solid.isValid()) {
  162. let otherBSP = createBSP(solid)
  163. switch (oper) {
  164. case 'union':
  165. resultBSP = resultBSP.union(otherBSP)
  166. break
  167. case 'intersect':
  168. resultBSP = resultBSP.intersect(otherBSP)
  169. break
  170. case 'subtract':
  171. resultBSP = resultBSP.subtract(otherBSP)
  172. break
  173. }
  174. }
  175. }
  176. let geometry = resultBSP.toSolidGeometry()
  177. let solid
  178. if (newSolid) {
  179. solid = new Solid()
  180. } else {
  181. let inverseMatrixWorld = new THREE.Matrix4()
  182. inverseMatrixWorld.copy(this.matrixWorld).invert()
  183. geometry.applyMatrix4(inverseMatrixWorld)
  184. solid = this
  185. }
  186. solid.updateGeometry(geometry, true)
  187. return solid
  188. }
  189. copy(source, recursive = true) {
  190. super.copy(source, false)
  191. // TODO: dispose geometries & materials?
  192. this._facesObject.geometry = source._facesObject.geometry
  193. this._edgesObject.geometry = source._edgesObject.geometry
  194. this.faceMaterial = source.faceMaterial
  195. this.edgeMaterial = source.edgeMaterial
  196. this.facesVisible = source.facesVisible
  197. this.edgesVisible = source.edgesVisible
  198. this.builder = source.builder ? source.builder.clone() : null
  199. this.updateMatrix()
  200. Formula.copy(this, source)
  201. if (recursive === true) {
  202. for (let i = 2; i < source.children.length; i++) {
  203. let child = source.children[i]
  204. this.add(child.clone())
  205. }
  206. }
  207. return this
  208. }
  209. raycast(raycaster, intersects) {
  210. if (this.facesVisible) {
  211. let solidInter = []
  212. THREE.Mesh.prototype.raycast.call(this._facesObject, raycaster, solidInter)
  213. for (let i = 0; i < solidInter.length; i++) {
  214. let intersection = solidInter[i]
  215. intersection.object = this
  216. intersects.push(intersection)
  217. }
  218. }
  219. }
  220. getArea() {
  221. let area = 0
  222. const geometry = this.geometry
  223. if (geometry) {
  224. const triangle = new THREE.Triangle()
  225. const matrixWorld = this.matrixWorld
  226. const vertices = geometry.vertices
  227. for (let face of geometry.faces) {
  228. let triangles = face.getTriangles()
  229. for (let tri of triangles) {
  230. let vertex0 = vertices[tri[0]]
  231. let vertex1 = vertices[tri[1]]
  232. let vertex2 = vertices[tri[2]]
  233. triangle.a.copy(vertex0).applyMatrix4(matrixWorld)
  234. triangle.b.copy(vertex1).applyMatrix4(matrixWorld)
  235. triangle.c.copy(vertex2).applyMatrix4(matrixWorld)
  236. area += triangle.getArea()
  237. }
  238. }
  239. }
  240. return area
  241. }
  242. getVolume() {
  243. let volume = 0
  244. const geometry = this.geometry
  245. if (geometry) {
  246. const matrixWorld = this.matrixWorld
  247. const vertex0 = new THREE.Vector3()
  248. const vertex1 = new THREE.Vector3()
  249. const vertex2 = new THREE.Vector3()
  250. const vertices = geometry.vertices
  251. for (let face of geometry.faces) {
  252. let triangles = face.getTriangles()
  253. for (let tri of triangles) {
  254. vertex0.copy(vertices[tri[0]]).applyMatrix4(matrixWorld)
  255. vertex1.copy(vertices[tri[1]]).applyMatrix4(matrixWorld)
  256. vertex2.copy(vertices[tri[2]]).applyMatrix4(matrixWorld)
  257. volume += vertex0.dot(vertex1.cross(vertex2)) / 6.0
  258. }
  259. }
  260. }
  261. return volume
  262. }
  263. updateGeometry(geometry, optimize = false) {
  264. this._facesObject.geometry.dispose()
  265. this._edgesObject.geometry.dispose()
  266. let solidGeometry
  267. if (geometry instanceof SolidGeometry) {
  268. solidGeometry = geometry
  269. } else {
  270. solidGeometry = new SolidGeometry()
  271. solidGeometry.copy(geometry)
  272. }
  273. if (optimize) {
  274. let optimizer = new SolidOptimizer(solidGeometry)
  275. solidGeometry = optimizer.optimize()
  276. }
  277. solidGeometry.updateBuffers()
  278. this._facesObject.geometry = solidGeometry
  279. this._edgesObject.geometry = solidGeometry.getEdgesGeometry()
  280. }
  281. }
  282. export { Solid }