Menubar.Add.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. import * as THREE from 'three';
  2. import { UIPanel, UIRow } from './libs/ui.js';
  3. import { AddObjectCommand } from './commands/AddObjectCommand.js';
  4. function MenubarAdd( editor ) {
  5. const strings = editor.strings;
  6. const container = new UIPanel();
  7. container.setClass( 'menu' );
  8. const title = new UIPanel();
  9. title.setClass( 'title' );
  10. title.setTextContent( strings.getKey( 'menubar/add' ) );
  11. container.add( title );
  12. const options = new UIPanel();
  13. options.setClass( 'options' );
  14. container.add( options );
  15. // Group
  16. let option = new UIRow();
  17. option.setClass( 'option' );
  18. option.setTextContent( strings.getKey( 'menubar/add/group' ) );
  19. option.onClick( function () {
  20. const mesh = new THREE.Group();
  21. mesh.name = 'Group';
  22. editor.execute( new AddObjectCommand( editor, mesh ) );
  23. } );
  24. options.add( option );
  25. // Mesh
  26. const meshSubmenuTitle = new UIRow().setTextContent( strings.getKey( 'menubar/add/mesh' ) ).addClass( 'option' ).addClass( 'submenu-title' );
  27. meshSubmenuTitle.onMouseOver( function () {
  28. const { top, right } = meshSubmenuTitle.dom.getBoundingClientRect();
  29. const { paddingTop } = getComputedStyle( this.dom );
  30. meshSubmenu.setLeft( right + 'px' );
  31. meshSubmenu.setTop( top - parseFloat( paddingTop ) + 'px' );
  32. meshSubmenu.setStyle( 'max-height', [ `calc( 100vh - ${top}px )` ] );
  33. meshSubmenu.setDisplay( 'block' );
  34. } );
  35. meshSubmenuTitle.onMouseOut( function () {
  36. meshSubmenu.setDisplay( 'none' );
  37. } );
  38. options.add( meshSubmenuTitle );
  39. const meshSubmenu = new UIPanel().setPosition( 'fixed' ).addClass( 'options' ).setDisplay( 'none' );
  40. meshSubmenuTitle.add( meshSubmenu );
  41. // Mesh / Box
  42. option = new UIRow();
  43. option.setClass( 'option' );
  44. option.setTextContent( strings.getKey( 'menubar/add/mesh/box' ) );
  45. option.onClick( function () {
  46. const geometry = new THREE.BoxGeometry( 1, 1, 1, 1, 1, 1 );
  47. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  48. mesh.name = 'Box';
  49. editor.execute( new AddObjectCommand( editor, mesh ) );
  50. } );
  51. meshSubmenu.add( option );
  52. // Mesh / Capsule
  53. option = new UIRow();
  54. option.setClass( 'option' );
  55. option.setTextContent( strings.getKey( 'menubar/add/mesh/capsule' ) );
  56. option.onClick( function () {
  57. const geometry = new THREE.CapsuleGeometry( 1, 1, 4, 8 );
  58. const material = new THREE.MeshStandardMaterial();
  59. const mesh = new THREE.Mesh( geometry, material );
  60. mesh.name = 'Capsule';
  61. editor.execute( new AddObjectCommand( editor, mesh ) );
  62. } );
  63. meshSubmenu.add( option );
  64. // Mesh / Circle
  65. option = new UIRow();
  66. option.setClass( 'option' );
  67. option.setTextContent( strings.getKey( 'menubar/add/mesh/circle' ) );
  68. option.onClick( function () {
  69. const geometry = new THREE.CircleGeometry( 1, 32, 0, Math.PI * 2 );
  70. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  71. mesh.name = 'Circle';
  72. editor.execute( new AddObjectCommand( editor, mesh ) );
  73. } );
  74. meshSubmenu.add( option );
  75. // Mesh / Cylinder
  76. option = new UIRow();
  77. option.setClass( 'option' );
  78. option.setTextContent( strings.getKey( 'menubar/add/mesh/cylinder' ) );
  79. option.onClick( function () {
  80. const geometry = new THREE.CylinderGeometry( 1, 1, 1, 32, 1, false, 0, Math.PI * 2 );
  81. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  82. mesh.name = 'Cylinder';
  83. editor.execute( new AddObjectCommand( editor, mesh ) );
  84. } );
  85. meshSubmenu.add( option );
  86. // Mesh / Dodecahedron
  87. option = new UIRow();
  88. option.setClass( 'option' );
  89. option.setTextContent( strings.getKey( 'menubar/add/mesh/dodecahedron' ) );
  90. option.onClick( function () {
  91. const geometry = new THREE.DodecahedronGeometry( 1, 0 );
  92. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  93. mesh.name = 'Dodecahedron';
  94. editor.execute( new AddObjectCommand( editor, mesh ) );
  95. } );
  96. meshSubmenu.add( option );
  97. // Mesh / Icosahedron
  98. option = new UIRow();
  99. option.setClass( 'option' );
  100. option.setTextContent( strings.getKey( 'menubar/add/mesh/icosahedron' ) );
  101. option.onClick( function () {
  102. const geometry = new THREE.IcosahedronGeometry( 1, 0 );
  103. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  104. mesh.name = 'Icosahedron';
  105. editor.execute( new AddObjectCommand( editor, mesh ) );
  106. } );
  107. meshSubmenu.add( option );
  108. // Mesh / Lathe
  109. option = new UIRow();
  110. option.setClass( 'option' );
  111. option.setTextContent( strings.getKey( 'menubar/add/mesh/lathe' ) );
  112. option.onClick( function () {
  113. const geometry = new THREE.LatheGeometry();
  114. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial( { side: THREE.DoubleSide } ) );
  115. mesh.name = 'Lathe';
  116. editor.execute( new AddObjectCommand( editor, mesh ) );
  117. } );
  118. meshSubmenu.add( option );
  119. // Mesh / Octahedron
  120. option = new UIRow();
  121. option.setClass( 'option' );
  122. option.setTextContent( strings.getKey( 'menubar/add/mesh/octahedron' ) );
  123. option.onClick( function () {
  124. const geometry = new THREE.OctahedronGeometry( 1, 0 );
  125. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  126. mesh.name = 'Octahedron';
  127. editor.execute( new AddObjectCommand( editor, mesh ) );
  128. } );
  129. meshSubmenu.add( option );
  130. // Mesh / Plane
  131. option = new UIRow();
  132. option.setClass( 'option' );
  133. option.setTextContent( strings.getKey( 'menubar/add/mesh/plane' ) );
  134. option.onClick( function () {
  135. const geometry = new THREE.PlaneGeometry( 1, 1, 1, 1 );
  136. const material = new THREE.MeshStandardMaterial();
  137. const mesh = new THREE.Mesh( geometry, material );
  138. mesh.name = 'Plane';
  139. editor.execute( new AddObjectCommand( editor, mesh ) );
  140. } );
  141. meshSubmenu.add( option );
  142. // Mesh / Ring
  143. option = new UIRow();
  144. option.setClass( 'option' );
  145. option.setTextContent( strings.getKey( 'menubar/add/mesh/ring' ) );
  146. option.onClick( function () {
  147. const geometry = new THREE.RingGeometry( 0.5, 1, 32, 1, 0, Math.PI * 2 );
  148. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  149. mesh.name = 'Ring';
  150. editor.execute( new AddObjectCommand( editor, mesh ) );
  151. } );
  152. meshSubmenu.add( option );
  153. // Mesh / Sphere
  154. option = new UIRow();
  155. option.setClass( 'option' );
  156. option.setTextContent( strings.getKey( 'menubar/add/mesh/sphere' ) );
  157. option.onClick( function () {
  158. const geometry = new THREE.SphereGeometry( 1, 32, 16, 0, Math.PI * 2, 0, Math.PI );
  159. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  160. mesh.name = 'Sphere';
  161. editor.execute( new AddObjectCommand( editor, mesh ) );
  162. } );
  163. meshSubmenu.add( option );
  164. // Mesh / Sprite
  165. option = new UIRow();
  166. option.setClass( 'option' );
  167. option.setTextContent( strings.getKey( 'menubar/add/mesh/sprite' ) );
  168. option.onClick( function () {
  169. const sprite = new THREE.Sprite( new THREE.SpriteMaterial() );
  170. sprite.name = 'Sprite';
  171. editor.execute( new AddObjectCommand( editor, sprite ) );
  172. } );
  173. meshSubmenu.add( option );
  174. // Mesh / Tetrahedron
  175. option = new UIRow();
  176. option.setClass( 'option' );
  177. option.setTextContent( strings.getKey( 'menubar/add/mesh/tetrahedron' ) );
  178. option.onClick( function () {
  179. const geometry = new THREE.TetrahedronGeometry( 1, 0 );
  180. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  181. mesh.name = 'Tetrahedron';
  182. editor.execute( new AddObjectCommand( editor, mesh ) );
  183. } );
  184. meshSubmenu.add( option );
  185. // Mesh / Torus
  186. option = new UIRow();
  187. option.setClass( 'option' );
  188. option.setTextContent( strings.getKey( 'menubar/add/mesh/torus' ) );
  189. option.onClick( function () {
  190. const geometry = new THREE.TorusGeometry( 1, 0.4, 12, 48, Math.PI * 2 );
  191. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  192. mesh.name = 'Torus';
  193. editor.execute( new AddObjectCommand( editor, mesh ) );
  194. } );
  195. meshSubmenu.add( option );
  196. // Mesh / TorusKnot
  197. option = new UIRow();
  198. option.setClass( 'option' );
  199. option.setTextContent( strings.getKey( 'menubar/add/mesh/torusknot' ) );
  200. option.onClick( function () {
  201. const geometry = new THREE.TorusKnotGeometry( 1, 0.4, 64, 8, 2, 3 );
  202. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  203. mesh.name = 'TorusKnot';
  204. editor.execute( new AddObjectCommand( editor, mesh ) );
  205. } );
  206. meshSubmenu.add( option );
  207. // Mesh / Tube
  208. option = new UIRow();
  209. option.setClass( 'option' );
  210. option.setTextContent( strings.getKey( 'menubar/add/mesh/tube' ) );
  211. option.onClick( function () {
  212. const path = new THREE.CatmullRomCurve3( [
  213. new THREE.Vector3( 2, 2, - 2 ),
  214. new THREE.Vector3( 2, - 2, - 0.6666666666666667 ),
  215. new THREE.Vector3( - 2, - 2, 0.6666666666666667 ),
  216. new THREE.Vector3( - 2, 2, 2 )
  217. ] );
  218. const geometry = new THREE.TubeGeometry( path, 64, 1, 8, false );
  219. const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
  220. mesh.name = 'Tube';
  221. editor.execute( new AddObjectCommand( editor, mesh ) );
  222. } );
  223. meshSubmenu.add( option );
  224. // Light
  225. const lightSubmenuTitle = new UIRow().setTextContent( strings.getKey( 'menubar/add/light' ) ).addClass( 'option' ).addClass( 'submenu-title' );
  226. lightSubmenuTitle.onMouseOver( function () {
  227. const { top, right } = lightSubmenuTitle.dom.getBoundingClientRect();
  228. const { paddingTop } = getComputedStyle( this.dom );
  229. lightSubmenu.setLeft( right + 'px' );
  230. lightSubmenu.setTop( top - parseFloat( paddingTop ) + 'px' );
  231. lightSubmenu.setStyle( 'max-height', [ `calc( 100vh - ${top}px )` ] );
  232. lightSubmenu.setDisplay( 'block' );
  233. } );
  234. lightSubmenuTitle.onMouseOut( function () {
  235. lightSubmenu.setDisplay( 'none' );
  236. } );
  237. options.add( lightSubmenuTitle );
  238. const lightSubmenu = new UIPanel().setPosition( 'fixed' ).addClass( 'options' ).setDisplay( 'none' );
  239. lightSubmenuTitle.add( lightSubmenu );
  240. // Light / Ambient
  241. option = new UIRow();
  242. option.setClass( 'option' );
  243. option.setTextContent( strings.getKey( 'menubar/add/light/ambient' ) );
  244. option.onClick( function () {
  245. const color = 0x222222;
  246. const light = new THREE.AmbientLight( color );
  247. light.name = 'AmbientLight';
  248. editor.execute( new AddObjectCommand( editor, light ) );
  249. } );
  250. lightSubmenu.add( option );
  251. // Light / Directional
  252. option = new UIRow();
  253. option.setClass( 'option' );
  254. option.setTextContent( strings.getKey( 'menubar/add/light/directional' ) );
  255. option.onClick( function () {
  256. const color = 0xffffff;
  257. const intensity = 1;
  258. const light = new THREE.DirectionalLight( color, intensity );
  259. light.name = 'DirectionalLight';
  260. light.target.name = 'DirectionalLight Target';
  261. light.position.set( 5, 10, 7.5 );
  262. editor.execute( new AddObjectCommand( editor, light ) );
  263. } );
  264. lightSubmenu.add( option );
  265. // Light / Hemisphere
  266. option = new UIRow();
  267. option.setClass( 'option' );
  268. option.setTextContent( strings.getKey( 'menubar/add/light/hemisphere' ) );
  269. option.onClick( function () {
  270. const skyColor = 0x00aaff;
  271. const groundColor = 0xffaa00;
  272. const intensity = 1;
  273. const light = new THREE.HemisphereLight( skyColor, groundColor, intensity );
  274. light.name = 'HemisphereLight';
  275. light.position.set( 0, 10, 0 );
  276. editor.execute( new AddObjectCommand( editor, light ) );
  277. } );
  278. lightSubmenu.add( option );
  279. // Light / Point
  280. option = new UIRow();
  281. option.setClass( 'option' );
  282. option.setTextContent( strings.getKey( 'menubar/add/light/point' ) );
  283. option.onClick( function () {
  284. const color = 0xffffff;
  285. const intensity = 1;
  286. const distance = 0;
  287. const light = new THREE.PointLight( color, intensity, distance );
  288. light.name = 'PointLight';
  289. editor.execute( new AddObjectCommand( editor, light ) );
  290. } );
  291. lightSubmenu.add( option );
  292. // Light / Spot
  293. option = new UIRow();
  294. option.setClass( 'option' );
  295. option.setTextContent( strings.getKey( 'menubar/add/light/spot' ) );
  296. option.onClick( function () {
  297. const color = 0xffffff;
  298. const intensity = 1;
  299. const distance = 0;
  300. const angle = Math.PI * 0.1;
  301. const penumbra = 0;
  302. const light = new THREE.SpotLight( color, intensity, distance, angle, penumbra );
  303. light.name = 'SpotLight';
  304. light.target.name = 'SpotLight Target';
  305. light.position.set( 5, 10, 7.5 );
  306. editor.execute( new AddObjectCommand( editor, light ) );
  307. } );
  308. lightSubmenu.add( option );
  309. // Camera
  310. const cameraSubmenuTitle = new UIRow().setTextContent( strings.getKey( 'menubar/add/camera' ) ).addClass( 'option' ).addClass( 'submenu-title' );
  311. cameraSubmenuTitle.onMouseOver( function () {
  312. const { top, right } = cameraSubmenuTitle.dom.getBoundingClientRect();
  313. const { paddingTop } = getComputedStyle( this.dom );
  314. cameraSubmenu.setLeft( right + 'px' );
  315. cameraSubmenu.setTop( top - parseFloat( paddingTop ) + 'px' );
  316. cameraSubmenu.setStyle( 'max-height', [ `calc( 100vh - ${top}px )` ] );
  317. cameraSubmenu.setDisplay( 'block' );
  318. } );
  319. cameraSubmenuTitle.onMouseOut( function () {
  320. cameraSubmenu.setDisplay( 'none' );
  321. } );
  322. options.add( cameraSubmenuTitle );
  323. const cameraSubmenu = new UIPanel().setPosition( 'fixed' ).addClass( 'options' ).setDisplay( 'none' );
  324. cameraSubmenuTitle.add( cameraSubmenu );
  325. // Camera / Orthographic
  326. option = new UIRow();
  327. option.setClass( 'option' );
  328. option.setTextContent( strings.getKey( 'menubar/add/camera/orthographic' ) );
  329. option.onClick( function () {
  330. const aspect = editor.camera.aspect;
  331. const camera = new THREE.OrthographicCamera( - aspect, aspect );
  332. camera.name = 'OrthographicCamera';
  333. editor.execute( new AddObjectCommand( editor, camera ) );
  334. } );
  335. cameraSubmenu.add( option );
  336. // Camera / Perspective
  337. option = new UIRow();
  338. option.setClass( 'option' );
  339. option.setTextContent( strings.getKey( 'menubar/add/camera/perspective' ) );
  340. option.onClick( function () {
  341. const camera = new THREE.PerspectiveCamera();
  342. camera.name = 'PerspectiveCamera';
  343. editor.execute( new AddObjectCommand( editor, camera ) );
  344. } );
  345. cameraSubmenu.add( option );
  346. return container;
  347. }
  348. export { MenubarAdd };