threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

LineMaterial2.ts 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import {generateUiConfig, uiColor, uiInput, uiNumber, UiObjectConfig, uiToggle, uiVector} from 'uiconfig.js'
  2. import {
  3. BaseEvent,
  4. BufferGeometry,
  5. Camera,
  6. Color,
  7. IUniform,
  8. Material,
  9. Object3D,
  10. Scene,
  11. Shader,
  12. Vector2,
  13. WebGLRenderer,
  14. } from 'three'
  15. import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
  16. import {
  17. IMaterial,
  18. IMaterialEventMap,
  19. IMaterialGenerator,
  20. IMaterialParameters,
  21. IMaterialTemplate,
  22. IMaterialUserData,
  23. } from '../IMaterial'
  24. import {MaterialExtension} from '../../materials'
  25. import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
  26. import {IObject3D} from '../IObject'
  27. import {iMaterialUI} from './IMaterialUi'
  28. import {LineMaterial, type LineMaterialParameters} from 'three/examples/jsm/lines/LineMaterial.js'
  29. /**
  30. * And extension of three.js LineMaterial that can be assigned to lines, and support threepipe features, uiconfig, and serialization.
  31. *
  32. * @category Materials
  33. */
  34. export class LineMaterial2<TE extends IMaterialEventMap = IMaterialEventMap> extends LineMaterial<TE> implements IMaterial<TE> {
  35. declare ['constructor']: typeof LineMaterial2
  36. public static readonly TypeSlug = 'lmat'
  37. public static readonly TYPE = 'LineMaterial2' // not using .type because it is used by three.js
  38. assetType = 'material' as const
  39. declare userData: IMaterialUserData
  40. public readonly isLineMaterial2 = true
  41. readonly appliedMeshes: Set<IObject3D> = new Set()
  42. readonly setDirty = iMaterialCommons.setDirty
  43. dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
  44. clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
  45. dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}
  46. generator?: IMaterialGenerator
  47. constructor({customMaterialExtensions, ...parameters}: LineMaterialParameters & IMaterialParameters = {}) {
  48. super()
  49. this.fog = false
  50. this.setDirty = this.setDirty.bind(this)
  51. if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions)
  52. iMaterialCommons.upgradeMaterial.call(this)
  53. this.setValues(parameters)
  54. // this.userData.renderToGBuffer = false
  55. // this.userData.renderToDepth = false
  56. }
  57. // region Material Extension
  58. materialExtensions: MaterialExtension[] = []
  59. extraUniformsToUpload: Record<string, IUniform> = {}
  60. registerMaterialExtensions = iMaterialCommons.registerMaterialExtensions
  61. unregisterMaterialExtensions = iMaterialCommons.unregisterMaterialExtensions
  62. customProgramCacheKey(): string {
  63. return super.customProgramCacheKey() + iMaterialCommons.customProgramCacheKey.call(this)
  64. }
  65. onBeforeCompile(shader: Shader, renderer: WebGLRenderer): void { // shader is not Shader but WebglUniforms.getParameters return value type so includes defines
  66. const f = [
  67. ['vec4 diffuseColor = ', 'beforeAccumulation'],
  68. ['#include <clipping_planes_fragment>', 'mainStart'],
  69. ]
  70. const v = [
  71. ['#ifdef USE_COLOR', 'mainStart'],
  72. ]
  73. for (const vElement of v) shader.vertexShader = shaderReplaceString(shader.vertexShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0])
  74. for (const fElement of f) shader.fragmentShader = shaderReplaceString(shader.fragmentShader, fElement[0], '#glMarker ' + fElement[1] + '\n' + fElement[0])
  75. iMaterialCommons.onBeforeCompile.call(this, shader, renderer)
  76. super.onBeforeCompile(shader, renderer)
  77. }
  78. autoUpdateResolution = true
  79. onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D): void {
  80. if (this.autoUpdateResolution) renderer.getSize(this.resolution)
  81. super.onBeforeRender(renderer, scene, camera, geometry, object)
  82. iMaterialCommons.onBeforeRender.call(this, renderer, scene, camera, geometry, object)
  83. }
  84. onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D): void {
  85. super.onAfterRender(renderer, scene, camera, geometry, object)
  86. iMaterialCommons.onAfterRender.call(this, renderer, scene, camera, geometry, object)
  87. }
  88. // endregion
  89. // region UI Config
  90. @uiInput() declare name: string
  91. @uiColor() declare color: Color
  92. @uiToggle() declare dashed: boolean
  93. @uiNumber() declare dashScale: number
  94. @uiNumber() declare dashSize: number
  95. @uiNumber() declare dashOffset: number
  96. @uiNumber() declare gapSize: number
  97. @uiNumber() declare linewidth: number
  98. @uiVector() declare resolution: Vector2
  99. @uiToggle() declare alphaToCoverage: boolean
  100. @uiToggle() declare worldUnits: boolean
  101. // @uiToggle() declare fog = true
  102. // todo dispose ui config
  103. uiConfig: UiObjectConfig = {
  104. type: 'folder',
  105. label: 'Line Material',
  106. uuid: 'MPM2_' + this.uuid,
  107. expanded: true,
  108. onChange: (ev)=>{
  109. if (!ev.config || ev.config.onChange) return
  110. // this.uniformsNeedUpdate = true
  111. // this.appliedMeshes.forEach(m=>{
  112. // if ((m.isLineSegments2 || m.isLineSegments) && m.computeLineDistances) {
  113. // m.computeLineDistances()
  114. // }
  115. // })
  116. // todo set needsUpdate true only for properties that require it like maps.
  117. this.setDirty({uiChangeEvent: ev, needsUpdate: !!ev.last, refreshUi: !!ev.last})
  118. },
  119. children: [
  120. ...generateUiConfig(this) || [],
  121. iMaterialUI.blending(this),
  122. iMaterialUI.polygonOffset(this),
  123. ...iMaterialUI.misc(this),
  124. ],
  125. }
  126. // endregion UI Config
  127. // region Serialization
  128. /**
  129. * Sets the values of this material based on the values of the passed material or an object with material properties
  130. * The input is expected to be a valid material or a deserialized material parameters object(including the deserialized userdata)
  131. * @param parameters - material or material parameters object
  132. * @param allowInvalidType - if true, the type of the oldMaterial is not checked. Objects without type are always allowed.
  133. * @param clearCurrentUserData - if undefined, then depends on material.isMaterial. if true, the current userdata is cleared before setting the new values, because it can have data which wont be overwritten if not present in the new material.
  134. */
  135. setValues(parameters: Material|(LineMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): this {
  136. if (!parameters) return this
  137. if (parameters.type && !allowInvalidType && !['LineMaterial', this.constructor.TYPE].includes(parameters.type) && !(parameters as LineMaterial2).isLineMaterial && !(parameters as LineMaterial2).isLineMaterial2) {
  138. console.error('Material type is not supported:', parameters.type)
  139. return this
  140. }
  141. if (clearCurrentUserData === undefined) clearCurrentUserData = (<Material>parameters).isMaterial
  142. if (clearCurrentUserData) this.userData = {}
  143. iMaterialCommons.setValues(super.setValues).call(this, parameters)
  144. this.userData.uuid = this.uuid
  145. return this
  146. }
  147. copy(source: Material|any): this {
  148. return this.setValues(source, false)
  149. }
  150. /**
  151. * Serializes this material to JSON.
  152. * @param meta - metadata for serialization
  153. * @param _internal - Calls only super.toJSON, does internal three.js serialization and `@serialize` tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material
  154. */
  155. toJSON(meta?: SerializationMetaType, _internal = false): any {
  156. if (_internal) return {
  157. ...super.toJSON(meta),
  158. ...ThreeSerialization.Serialize(this, meta, true), // this will serialize the properties of this class(like defined with @serialize and @serialize attribute)
  159. }
  160. return ThreeSerialization.Serialize(this, meta, false) // this will call toJSON again, but with baseOnly=true, that's why we set isThis to false.
  161. }
  162. /**
  163. * Deserializes the material from JSON.
  164. * Note: some properties that are not serialized in Material.toJSON when they are default values (like side, alphaTest, blending, maps), they wont be reverted back if not present in JSON
  165. * If _internal = true, Textures should be loaded and in meta.textures before calling this method.
  166. * @param data
  167. * @param meta
  168. * @param _internal
  169. */
  170. fromJSON(data: any, meta?: SerializationMetaType, _internal = false): this | null {
  171. if (_internal) {
  172. ThreeSerialization.Deserialize(data, this, meta, true)
  173. return this.setValues(data) // todo remove this and add @serialize decorator to properties
  174. }
  175. this.dispatchEvent({type: 'beforeDeserialize', data, meta, bubbleToObject: true, bubbleToParent: true})
  176. return this
  177. }
  178. // endregion
  179. // used for serialization and used in setValues
  180. static readonly MaterialProperties = {
  181. // keep updated with properties in LineMaterial.js
  182. ...threeMaterialPropList,
  183. color: new Color(0xffffff),
  184. dashed: false,
  185. dashScale: 1,
  186. dashSize: 1,
  187. dashOffset: 0,
  188. gapSize: 1,
  189. linewidth: 1,
  190. resolution: new Vector2(1, 1),
  191. alphaToCoverage: false,
  192. worldUnits: false,
  193. uniforms: {},
  194. defines: {},
  195. extensions: {},
  196. clipping: false,
  197. fog: true,
  198. fragmentShader: '',
  199. vertexShader: '',
  200. }
  201. static MaterialTemplate: IMaterialTemplate<LineMaterial2, Partial<typeof LineMaterial2.MaterialProperties>> = {
  202. materialType: LineMaterial2.TYPE,
  203. name: 'line',
  204. typeSlug: LineMaterial2.TypeSlug,
  205. alias: ['line', 'line_physical', LineMaterial2.TYPE, LineMaterial2.TypeSlug, 'LineMaterial'],
  206. params: {
  207. color: new Color(1, 1, 1),
  208. },
  209. generator: (params) => {
  210. return new LineMaterial2(params)
  211. },
  212. }
  213. }