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

ObjectShaderMaterial.ts 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import {BaseEvent, IUniform, Material, Shader, ShaderMaterial, ShaderMaterialParameters, WebGLRenderer} from 'three'
  2. import {generateUiConfig, UiObjectConfig} from 'uiconfig.js'
  3. import {
  4. IMaterial,
  5. IMaterialEventMap,
  6. IMaterialGenerator,
  7. IMaterialParameters,
  8. IMaterialTemplate,
  9. IMaterialUserData,
  10. } from '../IMaterial'
  11. import {MaterialExtension} from '../../materials'
  12. import {SerializationMetaType, shaderUtils, ThreeSerialization} from '../../utils'
  13. import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
  14. import {IObject3D} from '../IObject'
  15. import {iMaterialUI} from './IMaterialUi'
  16. /**
  17. * And extension of three.js ShaderMaterial that can be assigned to objects, and support threepipe features, uiconfig, and serialization.
  18. *
  19. * @category Materials
  20. */
  21. export class ObjectShaderMaterial<TE extends IMaterialEventMap = IMaterialEventMap> extends ShaderMaterial<TE> implements IMaterial<TE> {
  22. declare ['constructor']: typeof ObjectShaderMaterial
  23. public static readonly TypeSlug = 'shmat'
  24. public static readonly TYPE = 'ObjectShaderMaterial' // not using .type because it is used by three.js
  25. assetType = 'material' as const
  26. declare userData: IMaterialUserData
  27. public readonly isObjectShaderMaterial = true
  28. readonly appliedMeshes: Set<IObject3D> = new Set()
  29. readonly setDirty = iMaterialCommons.setDirty
  30. dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
  31. clone(track = false): this {return iMaterialCommons.clone(super.clone).call(this, track)}
  32. dispatchEvent<T extends Extract<keyof (TE&IMaterialEventMap), string>>(event: BaseEvent<T> & (TE&IMaterialEventMap)[T]): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}
  33. generator?: IMaterialGenerator
  34. // envMap: ITexture | null = null
  35. constructor({customMaterialExtensions, ...parameters}: ShaderMaterialParameters & IMaterialParameters = {}) {
  36. super()
  37. !this.defines && (this.defines = {})
  38. this.fog = false
  39. this.setDirty = this.setDirty.bind(this)
  40. if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions)
  41. iMaterialCommons.upgradeMaterial.call(this)
  42. this.setValues(parameters)
  43. }
  44. // region Material Extension
  45. materialExtensions: MaterialExtension[] = []
  46. extraUniformsToUpload: Record<string, IUniform> = {}
  47. registerMaterialExtensions = iMaterialCommons.registerMaterialExtensions
  48. unregisterMaterialExtensions = iMaterialCommons.unregisterMaterialExtensions
  49. customProgramCacheKey(): string {
  50. return super.customProgramCacheKey() + iMaterialCommons.customProgramCacheKey.call(this)
  51. }
  52. onBeforeCompile(shader: Shader, renderer: WebGLRenderer): void { // shader is not Shader but WebglUniforms.getParameters return value type so includes defines
  53. // const f = [
  54. // ['vec3 outgoingLight = ', 'afterModulation'], // added markers before found substring
  55. // ['#include <aomap_fragment>', 'beforeModulation'],
  56. // ['ReflectedLight reflectedLight = ', 'beforeAccumulation'],
  57. // ['#include <clipping_planes_fragment>', 'mainStart'],
  58. // ]
  59. // const v = [
  60. // ['#include <uv_vertex>', 'mainStart'],
  61. // ]
  62. //
  63. // for (const vElement of v) shader.vertexShader = shaderReplaceString(shader.vertexShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0])
  64. // for (const vElement of f) shader.fragmentShader = shaderReplaceString(shader.fragmentShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0])
  65. iMaterialCommons.onBeforeCompile.call(this, shader, renderer)
  66. // shader.defines.INVERSE_ALPHAMAP = this.userData.inverseAlphaMap ? 1 : 0
  67. super.onBeforeCompile(shader, renderer)
  68. }
  69. // onBeforeRender(...args: Parameters<IMaterial['onBeforeRender']>): void {
  70. // super.onBeforeRender(...args)
  71. // iMaterialCommons.onBeforeRender.call(this, ...args)
  72. // }
  73. onBeforeRender = iMaterialCommons.onBeforeRenderOverride(super.onBeforeRender)
  74. onAfterRender = iMaterialCommons.onAfterRenderOverride(super.onAfterRender)
  75. // endregion
  76. // region Serialization
  77. /**
  78. * Sets the values of this material based on the values of the passed material or an object with material properties
  79. * The input is expected to be a valid material or a deserialized material parameters object(including the deserialized userdata)
  80. * @param parameters - material or material parameters object
  81. * @param allowInvalidType - if true, the type of the oldMaterial is not checked. Objects without type are always allowed.
  82. * @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.
  83. */
  84. setValues(parameters: Material|(ShaderMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): this {
  85. if (!parameters) return this
  86. if (parameters.type && !allowInvalidType && !['ShaderMaterial', 'ShaderMaterial2', 'ExtendedShaderMaterial', this.constructor.TYPE].includes(parameters.type)) {
  87. console.error('Material type is not supported:', parameters.type)
  88. return this
  89. }
  90. if (clearCurrentUserData === undefined) clearCurrentUserData = (<Material>parameters).isMaterial
  91. if (clearCurrentUserData) this.userData = {}
  92. iMaterialCommons.setValues(super.setValues).call(this, parameters)
  93. this.userData.uuid = this.uuid
  94. return this
  95. }
  96. copy(source: Material|any): this {
  97. return this.setValues(source, false)
  98. }
  99. /**
  100. * Serializes this material to JSON.
  101. * @param meta - metadata for serialization
  102. * @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
  103. */
  104. toJSON(meta?: SerializationMetaType, _internal = false): any {
  105. if (_internal) return {
  106. ...super.toJSON(meta),
  107. ...ThreeSerialization.Serialize(this, meta, true), // this will serialize the properties of this class(like defined with @serialize and @serialize attribute)
  108. }
  109. return ThreeSerialization.Serialize(this, meta, false) // this will call toJSON again, but with _internal=true, that's why we set isThis to false.
  110. }
  111. /**
  112. * Deserializes the material from JSON.
  113. * Textures should be loaded and in meta.textures before calling this method.
  114. * todo - needs to be tested
  115. * @param data
  116. * @param meta
  117. * @param _internal
  118. */
  119. fromJSON(data: any, meta?: SerializationMetaType, _internal = false): this | null {
  120. if (_internal) {
  121. ThreeSerialization.Deserialize(data, this, meta, true)
  122. return this.setValues(data)
  123. }
  124. this.dispatchEvent({type: 'beforeDeserialize', data, meta, bubbleToObject: true, bubbleToParent: true})
  125. return this
  126. }
  127. // endregion
  128. // region UI Config
  129. // todo dispose ui config
  130. uiConfig: UiObjectConfig = {
  131. type: 'folder',
  132. label: 'Shader Material',
  133. uuid: 'OSM2_' + this.uuid,
  134. expanded: true,
  135. onChange: (ev)=>{
  136. if (!ev.config || ev.config.onChange) return
  137. let key = Array.isArray(ev.config.property) ? ev.config.property[1] : ev.config.property
  138. key = typeof key === 'string' ? key : undefined
  139. // todo set needsUpdate true only for properties that require it like maps.
  140. this.setDirty({uiChangeEvent: ev, needsUpdate: !!ev.last, refreshUi: !!ev.last, change: key})
  141. },
  142. children: [
  143. ...generateUiConfig(this),
  144. ...iMaterialUI.base(this),
  145. iMaterialUI.blending(this),
  146. iMaterialUI.polygonOffset(this),
  147. ...iMaterialUI.misc(this),
  148. ],
  149. }
  150. // endregion UI Config
  151. // Class properties can also be listed with annotations like @serialize or @property
  152. // used for serialization // todo change for shadermaterial
  153. static readonly MaterialProperties = {
  154. ...threeMaterialPropList,
  155. defines: {},
  156. uniforms: {},
  157. uniformsGroups: [],
  158. vertexShader: '',
  159. fragmentShader: '',
  160. linewidth: 1,
  161. wireframe: false,
  162. wireframeLinewidth: 1,
  163. fog: false, // set to use scene fog
  164. lights: false, // set to use scene lights
  165. clipping: false, // set to use user-defined clipping planes
  166. forceSinglePass: true,
  167. extensions: {
  168. derivatives: false, // set to use derivatives
  169. fragDepth: false, // set to use fragment depth values
  170. drawBuffers: false, // set to use draw buffers
  171. shaderTextureLOD: false, // set to use shader texture LOD
  172. },
  173. // When rendered geometry doesn't include these attributes but the material does,
  174. // use these default values in WebGL. This avoids errors when buffer data is missing.
  175. defaultAttributeValues: {
  176. 'color': [1, 1, 1],
  177. 'uv': [0, 0],
  178. 'uv1': [0, 0],
  179. },
  180. index0AttributeName: undefined,
  181. uniformsNeedUpdate: false,
  182. glslVersion: null,
  183. flatShading: false,
  184. }
  185. static MaterialTemplate: IMaterialTemplate<ObjectShaderMaterial, Partial<typeof ObjectShaderMaterial.MaterialProperties>> = {
  186. materialType: ObjectShaderMaterial.TYPE,
  187. name: 'shader',
  188. typeSlug: ObjectShaderMaterial.TypeSlug,
  189. alias: ['shader', ObjectShaderMaterial.TYPE, ObjectShaderMaterial.TypeSlug, 'ShaderMaterial', 'ShaderMaterial2', 'ExtendedShaderMaterial'],
  190. params: {
  191. vertexShader: shaderUtils.defaultVertex,
  192. fragmentShader: shaderUtils.defaultFragment,
  193. },
  194. generator: (params) => {
  195. return new ObjectShaderMaterial(params)
  196. },
  197. }
  198. }
  199. // todo gltf material extension