Преглед на файлове

Add UnlitLineMaterial and LineMaterial2, some IMaterial fixes and improvements. Add shaderUtils(to ShaderChunk) with simpleCameraHelpers, randomHelpers, voronoiNoise. Export updateMaterialDefines, support boolean define,

master
Palash Bansal преди 2 години
родител
ревизия
4e524efcf0
No account linked to committer's email address

+ 0
- 1
src/assetmanager/AssetManager.ts Целия файл

if (!data.metadata) { if (!data.metadata) {
console.warn('Invalid data(no metadata)', data) console.warn('Invalid data(no metadata)', data)
} }
console.log(data, event)
if (event.material) { if (event.material) {
if (data.metadata?.type !== 'Material') { if (data.metadata?.type !== 'Material') {
console.warn('Invalid material data', data) console.warn('Invalid material data', data)

+ 6
- 1
src/assetmanager/MaterialManager.ts Целия файл

IMaterialTemplate, IMaterialTemplate,
ITexture, ITexture,
ITextureEvent, ITextureEvent,
LegacyPhongMaterial,
LineMaterial2,
PhysicalMaterial, PhysicalMaterial,
UnlitLineMaterial,
UnlitMaterial, UnlitMaterial,
} from '../core' } from '../core'
import {downloadFile} from 'ts-browser-helpers' import {downloadFile} from 'ts-browser-helpers'
import {MaterialExtension} from '../materials' import {MaterialExtension} from '../materials'
import {generateUUID, isInScene} from '../three' import {generateUUID, isInScene} from '../three'
import {LegacyPhongMaterial} from '../core/material/LegacyPhongMaterial'


/** /**
* Material Manager * Material Manager
readonly templates: IMaterialTemplate[] = [ readonly templates: IMaterialTemplate[] = [
PhysicalMaterial.MaterialTemplate, PhysicalMaterial.MaterialTemplate,
UnlitMaterial.MaterialTemplate, UnlitMaterial.MaterialTemplate,
UnlitLineMaterial.MaterialTemplate,
LineMaterial2.MaterialTemplate,
LegacyPhongMaterial.MaterialTemplate, LegacyPhongMaterial.MaterialTemplate,
] ]


} }
return applied return applied
} }

} }



+ 1
- 1
src/core/IMaterial.ts Целия файл



inverseAlphaMap?: boolean // only for physical material right now inverseAlphaMap?: boolean // only for physical material right now


[key: string]: any // commented for noe
[key: string]: any




// legacy, to be removed // legacy, to be removed

+ 1
- 0
src/core/IObject.ts Целия файл

isCamera?: boolean isCamera?: boolean
isMesh?: boolean isMesh?: boolean
isLine?: boolean isLine?: boolean
isLineSegments?: boolean
// isGroup?: boolean // isGroup?: boolean
isScene?: boolean isScene?: boolean
// isHelper?: boolean // isHelper?: boolean

+ 2
- 0
src/core/index.ts Целия файл

export {PhysicalMaterial, type PhysicalMaterialEventTypes, MeshStandardMaterial2} from './material/PhysicalMaterial' export {PhysicalMaterial, type PhysicalMaterialEventTypes, MeshStandardMaterial2} from './material/PhysicalMaterial'
export {ShaderMaterial2} from './material/ShaderMaterial2' export {ShaderMaterial2} from './material/ShaderMaterial2'
export {UnlitMaterial, type UnlitMaterialEventTypes, MeshBasicMaterial2} from './material/UnlitMaterial' export {UnlitMaterial, type UnlitMaterialEventTypes, MeshBasicMaterial2} from './material/UnlitMaterial'
export {UnlitLineMaterial, type UnlitLineMaterialEventTypes, LineBasicMaterial2} from './material/UnlitLineMaterial'
export {LineMaterial2, type LineMaterial2EventTypes} from './material/LineMaterial2'
export {LegacyPhongMaterial, type PhongMaterialEventTypes} from './material/LegacyPhongMaterial' export {LegacyPhongMaterial, type PhongMaterialEventTypes} from './material/LegacyPhongMaterial'
export {iObjectCommons} from './object/iObjectCommons' export {iObjectCommons} from './object/iObjectCommons'
export {iCameraCommons} from './object/iCameraCommons' export {iCameraCommons} from './object/iCameraCommons'

+ 3
- 0
src/core/material/ExtendedShaderMaterial.ts Целия файл

WebGLRenderer, WebGLRenderer,
} from 'three' } from 'three'
import {shaderReplaceString} from '../../utils' import {shaderReplaceString} from '../../utils'
import {IMaterialUserData} from '../IMaterial'


export class ExtendedShaderMaterial extends ShaderMaterial2 { export class ExtendedShaderMaterial extends ShaderMaterial2 {
declare ['constructor']: (typeof ExtendedShaderMaterial) & (typeof ShaderMaterial2) declare ['constructor']: (typeof ExtendedShaderMaterial) & (typeof ShaderMaterial2)


textures: {colorSpace: ColorSpace, id: string}[] = [] textures: {colorSpace: ColorSpace, id: string}[] = []


userData: IMaterialUserData

constructor(parameters: ShaderMaterialParameters, textureIds: string[], isRawShaderMaterial = false) { constructor(parameters: ShaderMaterialParameters, textureIds: string[], isRawShaderMaterial = false) {
super(parameters, isRawShaderMaterial) super(parameters, isRawShaderMaterial)
this.setTextureIds(textureIds) this.setTextureIds(textureIds)

+ 18
- 6
src/core/material/IMaterialUi.ts Целия файл

} from 'three' } from 'three'
import {downloadBlob, uploadFile} from 'ts-browser-helpers' import {downloadBlob, uploadFile} from 'ts-browser-helpers'
import {PhysicalMaterial} from './PhysicalMaterial' import {PhysicalMaterial} from './PhysicalMaterial'
import {getEmptyMeta} from '../../utils/serialization'
import {getEmptyMeta} from '../../utils'
import {LegacyPhongMaterial} from './LegacyPhongMaterial' import {LegacyPhongMaterial} from './LegacyPhongMaterial'
import {generateUUID} from '../../three'

declare module '../IMaterial' {
interface IMaterial {
__matExtUiConfigs?: Record<string, UiObjectConfig|undefined>
}
}


export const iMaterialUI = { export const iMaterialUI = {
base: (material: IMaterial): UiObjectConfig[] => [ base: (material: IMaterial): UiObjectConfig[] => [
value: value[1], value: value[1],
})), })),
}, },
{
material.alphaMap !== undefined ? {
type: 'image', type: 'image',
property: [material, 'alphaMap'], property: [material, 'alphaMap'],
},
makeSamplerUi(material, 'alphaMap'),
} : {},
material.alphaMap !== undefined ? makeSamplerUi(material, 'alphaMap') : {},
{ {
type: 'checkbox', type: 'checkbox',
label: 'Render to Gbuffer', label: 'Render to Gbuffer',
} }
), ),
misc: (material: IMaterial): UiObjectConfig[] => [ misc: (material: IMaterial): UiObjectConfig[] => [
()=>material.materialExtensions?.map(v=>{
v.uuid = v.uuid || generateUUID()
material.__matExtUiConfigs = material.__matExtUiConfigs || {}
if (!material.__matExtUiConfigs[v.uuid]) material.__matExtUiConfigs[v.uuid] = v.getUiConfig?.(material, material.uiConfig?.uiRefresh)
return material.__matExtUiConfigs[v.uuid]
}).filter(v=>v),
{ {
type: 'dropdown', type: 'dropdown',
label: 'Side', label: 'Side',
}) })
}, },
}, },
()=>material.materialExtensions?.map(v=>v.getUiConfig?.(material, material.uiConfig?.uiRefresh)).filter(v=>v),
], ],
roughMetal: (material: PhysicalMaterial): UiObjectConfig => ( roughMetal: (material: PhysicalMaterial): UiObjectConfig => (
{ {
children: [ children: [
{ {
type: 'slider', type: 'slider',
bounds: [0, 0.2],
bounds: [-0.2, 0.2],
stepSize: 0.001, stepSize: 0.001,
property: [material, 'bumpScale'], property: [material, 'bumpScale'],
hidden: ()=>!material.bumpMap, hidden: ()=>!material.bumpMap,

+ 3
- 0
src/core/material/LegacyPhongMaterial.ts Целия файл

IMaterialGenerator, IMaterialGenerator,
IMaterialParameters, IMaterialParameters,
IMaterialTemplate, IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial' } from '../IMaterial'
import {MaterialExtension} from '../../materials' import {MaterialExtension} from '../../materials'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils' import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
public static readonly TYPE = 'LegacyPhongMaterial' // not using .type because it is used by three.js public static readonly TYPE = 'LegacyPhongMaterial' // not using .type because it is used by three.js
assetType = 'material' as const assetType = 'material' as const


userData: IMaterialUserData

public readonly isLegacyPhongMaterial = true public readonly isLegacyPhongMaterial = true


readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()

+ 210
- 0
src/core/material/LineMaterial2.ts Целия файл

import {generateUiConfig, uiColor, uiInput, uiNumber, UiObjectConfig, uiToggle, uiVector} from 'uiconfig.js'
import {Color, IUniform, Material, Shader, Vector2, WebGLRenderer} from 'three'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
import {
IMaterial,
IMaterialEvent,
IMaterialEventTypes,
IMaterialGenerator,
IMaterialParameters,
IMaterialTemplate,
} from '../IMaterial'
import {MaterialExtension} from '../../materials'
import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
import {IObject3D} from '../IObject'
import {iMaterialUI} from './IMaterialUi'
import {LineMaterial, type LineMaterialParameters} from 'three/examples/jsm/lines/LineMaterial.js'

export type LineMaterial2EventTypes = IMaterialEventTypes | ''

export class LineMaterial2 extends LineMaterial<IMaterialEvent, LineMaterial2EventTypes> implements IMaterial<IMaterialEvent, LineMaterial2EventTypes> {
declare ['constructor']: typeof LineMaterial2
public static readonly TypeSlug = 'lmat'
public static readonly TYPE = 'LineMaterial2' // not using .type because it is used by three.js
assetType = 'material' as const

public readonly isLineMaterial2 = true

readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}

generator?: IMaterialGenerator

constructor({customMaterialExtensions, ...parameters}: LineMaterialParameters & IMaterialParameters = {}) {
super(parameters)
this.fog = false
this.setDirty = this.setDirty.bind(this)
if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions)
iMaterialCommons.upgradeMaterial.call(this)
}

// region Material Extension

materialExtensions: MaterialExtension[] = []
extraUniformsToUpload: Record<string, IUniform> = {}
registerMaterialExtensions = iMaterialCommons.registerMaterialExtensions
unregisterMaterialExtensions = iMaterialCommons.unregisterMaterialExtensions

customProgramCacheKey(): string {
return super.customProgramCacheKey() + iMaterialCommons.customProgramCacheKey.call(this)
}

onBeforeCompile(shader: Shader, renderer: WebGLRenderer): void { // shader is not Shader but WebglUniforms.getParameters return value type so includes defines
const f = [
['vec4 diffuseColor = ', 'beforeAccumulation'],
['#include <clipping_planes_fragment>', 'mainStart'],
]
const v = [
['#ifdef USE_COLOR', 'mainStart'],
]
for (const vElement of v) shader.vertexShader = shaderReplaceString(shader.vertexShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0])
for (const fElement of f) shader.fragmentShader = shaderReplaceString(shader.fragmentShader, fElement[0], '#glMarker ' + fElement[1] + '\n' + fElement[0])

iMaterialCommons.onBeforeCompile.call(this, shader, renderer)

super.onBeforeCompile(shader, renderer)
}

onAfterRender = iMaterialCommons.onAfterRenderOverride(super.onAfterRender)

// endregion


// region UI Config

@uiInput() name: string
@uiColor() color: Color
@uiToggle() dashed: boolean
@uiNumber() dashScale: number
@uiNumber() dashSize: number
@uiNumber() dashOffset: number
@uiNumber() gapSize: number
@uiNumber() linewidth: number
@uiVector() resolution: Vector2
@uiToggle() alphaToCoverage: boolean
@uiToggle() worldUnits: boolean
// @uiToggle() fog = true


// todo dispose ui config
uiConfig: UiObjectConfig = {
type: 'folder',
label: 'Line Material',
uuid: 'MPM2_' + this.uuid,
expanded: true,
onChange: (ev)=>{
if (!ev.config || ev.config.onChange) return
this.setDirty({uiChangeEvent: ev, needsUpdate: false, refreshUi: true})
},
children: [
...generateUiConfig(this),
iMaterialUI.blending(this),
iMaterialUI.polygonOffset(this),
...iMaterialUI.misc(this),
],
}

// endregion UI Config


// region Serialization

/**
* Sets the values of this material based on the values of the passed material or an object with material properties
* The input is expected to be a valid material or a deserialized material parameters object(including the deserialized userdata)
* @param parameters - material or material parameters object
* @param allowInvalidType - if true, the type of the oldMaterial is not checked. Objects without type are always allowed.
* @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.
*/
setValues(parameters: Material|(LineMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): this {
if (!parameters) return this
if (parameters.type && !allowInvalidType && !['LineMaterial', this.constructor.TYPE].includes(parameters.type)) {
console.error('Material type is not supported:', parameters.type)
return this
}
if (clearCurrentUserData === undefined) clearCurrentUserData = (<Material>parameters).isMaterial
if (clearCurrentUserData) this.userData = {}
iMaterialCommons.setValues(super.setValues).call(this, parameters)

this.userData.uuid = this.uuid
return this
}

copy(source: Material|any): this {
return this.setValues(source, false)
}

/**
* Serializes this material to JSON.
* @param meta - metadata for serialization
* @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
*/
toJSON(meta?: SerializationMetaType, _internal = false): any {
if (_internal) return {
...super.toJSON(meta),
...ThreeSerialization.Serialize(this, meta, true), // this will serialize the properties of this class(like defined with @serialize and @serialize attribute)
}
return ThreeSerialization.Serialize(this, meta, false) // this will call toJSON again, but with baseOnly=true, that's why we set isThis to false.
}

/**
* Deserializes the material from JSON.
* 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
* If _internal = true, Textures should be loaded and in meta.textures before calling this method.
* @param data
* @param meta
* @param _internal
*/
fromJSON(data: any, meta?: SerializationMetaType, _internal = false): this | null {
if (_internal) {
ThreeSerialization.Deserialize(data, this, meta, true)
return this.setValues(data) // todo remove this and add @serialize decorator to properties
}
this.dispatchEvent({type: 'beforeDeserialize', data, meta, bubbleToObject: true, bubbleToParent: true})
return this
}

// endregion

// used for serialization and used in setValues
static readonly MaterialProperties = {
// keep updated with properties in LineMaterial.js
...threeMaterialPropList,

color: new Color(0xffffff),
dashed: false,
dashScale: 1,
dashSize: 1,
dashOffset: 0,
gapSize: 1,
linewidth: 1,
resolution: new Vector2(1, 1),
alphaToCoverage: false,
worldUnits: false,

uniforms: {},
defines: {},
extensions: {},
clipping: false,
fog: true,
fragmentShader: '',
vertexShader: '',

}

static MaterialTemplate: IMaterialTemplate<LineMaterial2, Partial<typeof LineMaterial2.MaterialProperties>> = {
materialType: LineMaterial2.TYPE,
name: 'line',
typeSlug: LineMaterial2.TypeSlug,
alias: ['line', 'line_physical', LineMaterial2.TYPE, LineMaterial2.TypeSlug, 'LineMaterial'],
params: {
color: new Color(1, 1, 1),
},
generator: (params) => {
return new LineMaterial2(params)
},
}
}

+ 4
- 2
src/core/material/PhysicalMaterial.ts Целия файл

Vector2, Vector2,
WebGLRenderer, WebGLRenderer,
} from 'three' } from 'three'
import {shaderReplaceString} from '../../utils/shader-helpers'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
import { import {
IMaterial, IMaterial,
IMaterialEvent, IMaterialEvent,
IMaterialGenerator, IMaterialGenerator,
IMaterialParameters, IMaterialParameters,
IMaterialTemplate, IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial' } from '../IMaterial'
import {SerializationMetaType, ThreeSerialization} from '../../utils/serialization'
import {MaterialExtension} from '../../materials' import {MaterialExtension} from '../../materials'
import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons' import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
import {IObject3D} from '../IObject' import {IObject3D} from '../IObject'
public static readonly TYPE = 'PhysicalMaterial' // not using .type because it is used by three.js public static readonly TYPE = 'PhysicalMaterial' // not using .type because it is used by three.js
assetType = 'material' as const assetType = 'material' as const


userData: IMaterialUserData

public readonly isPhysicalMaterial = true public readonly isPhysicalMaterial = true


readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()

+ 4
- 1
src/core/material/ShaderMaterial2.ts Целия файл

ShaderMaterialParameters, ShaderMaterialParameters,
WebGLRenderer, WebGLRenderer,
} from 'three' } from 'three'
import {IMaterial, IMaterialEvent, IMaterialEventTypes, IMaterialParameters} from '../IMaterial'
import {IMaterial, IMaterialEvent, IMaterialEventTypes, IMaterialParameters, IMaterialUserData} from '../IMaterial'
import {MaterialExtension} from '../../materials' import {MaterialExtension} from '../../materials'
import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons' import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'


} }


assetType = 'material' as const assetType = 'material' as const

userData: IMaterialUserData

public readonly isAShaderMaterial = true public readonly isAShaderMaterial = true


readonly appliedMeshes: Set<any> = new Set() readonly appliedMeshes: Set<any> = new Set()

+ 227
- 0
src/core/material/UnlitLineMaterial.ts Целия файл

import {Color, IUniform, LineBasicMaterial, LineBasicMaterialParameters, Material, Shader, WebGLRenderer} from 'three'
import {UiObjectConfig} from 'uiconfig.js'
import {
IMaterial,
IMaterialEvent,
IMaterialEventTypes,
IMaterialGenerator,
IMaterialParameters,
IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial'
import {MaterialExtension} from '../../materials'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
import {IObject3D} from '../IObject'
import {makeSamplerUi} from '../../ui/image-ui'
import {iMaterialUI} from './IMaterialUi'

export type UnlitLineMaterialEventTypes = IMaterialEventTypes | ''

export class UnlitLineMaterial extends LineBasicMaterial<IMaterialEvent, UnlitLineMaterialEventTypes> implements IMaterial<IMaterialEvent, UnlitLineMaterialEventTypes> {
declare ['constructor']: typeof UnlitLineMaterial

public static readonly TypeSlug = 'blmat'
public static readonly TYPE = 'UnlitLineMaterial' // not using .type because it is used by three.js
assetType = 'material' as const

userData: IMaterialUserData

public readonly isUnlitLineMaterial = true

readonly appliedMeshes: Set<IObject3D> = new Set()
readonly setDirty = iMaterialCommons.setDirty
dispose(): this {return iMaterialCommons.dispose(super.dispose).call(this)}
clone(): this {return iMaterialCommons.clone(super.clone).call(this)}
dispatchEvent(event: IMaterialEvent): void {iMaterialCommons.dispatchEvent(super.dispatchEvent).call(this, event)}

generator?: IMaterialGenerator

constructor({customMaterialExtensions, ...parameters}: LineBasicMaterialParameters & IMaterialParameters = {}) {
super(parameters)
!this.defines && (this.defines = {})
this.fog = false
this.setDirty = this.setDirty.bind(this)
if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions)
iMaterialCommons.upgradeMaterial.call(this)
}

// region Material Extension

materialExtensions: MaterialExtension[] = []
extraUniformsToUpload: Record<string, IUniform> = {}
registerMaterialExtensions = iMaterialCommons.registerMaterialExtensions
unregisterMaterialExtensions = iMaterialCommons.unregisterMaterialExtensions

customProgramCacheKey(): string {
return super.customProgramCacheKey() + iMaterialCommons.customProgramCacheKey.call(this)
}

onBeforeCompile(shader: Shader, renderer: WebGLRenderer): void { // shader is not Shader but WebglUniforms.getParameters return value type so includes defines
const f = [
['vec3 outgoingLight = ', 'afterModulation'], // added markers before found substring
['#include <aomap_fragment>', 'beforeModulation'],
['ReflectedLight reflectedLight = ', 'beforeAccumulation'],
['#include <clipping_planes_fragment>', 'mainStart'],
]
const v = [
['#include <uv_vertex>', 'mainStart'],
]

for (const vElement of v) shader.vertexShader = shaderReplaceString(shader.vertexShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0])
for (const vElement of f) shader.fragmentShader = shaderReplaceString(shader.fragmentShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0])

iMaterialCommons.onBeforeCompile.call(this, shader, renderer)

super.onBeforeCompile(shader, renderer)
}

onBeforeRender = iMaterialCommons.onBeforeRenderOverride(super.onBeforeRender)
onAfterRender = iMaterialCommons.onAfterRenderOverride(super.onAfterRender)

// endregion

// region Serialization

/**
* Sets the values of this material based on the values of the passed material or an object with material properties
* The input is expected to be a valid material or a deserialized material parameters object(including the deserialized userdata)
* @param parameters - material or material parameters object
* @param allowInvalidType - if true, the type of the oldMaterial is not checked. Objects without type are always allowed.
* @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.
*/
setValues(parameters: Material|(LineBasicMaterialParameters&{type?:string}), allowInvalidType = true, clearCurrentUserData: boolean|undefined = undefined): this {
if (!parameters) return this
if (parameters.type && !allowInvalidType && !['LineBasicMaterial', 'LineBasicMaterial2', this.constructor.TYPE].includes(parameters.type)) {
console.error('Material type is not supported:', parameters.type)
return this
}
if (clearCurrentUserData === undefined) clearCurrentUserData = (<Material>parameters).isMaterial
if (clearCurrentUserData) this.userData = {}
iMaterialCommons.setValues(super.setValues).call(this, parameters)

this.userData.uuid = this.uuid
return this
}
copy(source: Material|any): this {
return this.setValues(source, false)
}

/**
* Serializes this material to JSON.
* @param meta - metadata for serialization
* @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
*/
toJSON(meta?: SerializationMetaType, _internal = false): any {
if (_internal) return {
...super.toJSON(meta),
...ThreeSerialization.Serialize(this, meta, true), // this will serialize the properties of this class(like defined with @serialize and @serialize attribute)
}
return ThreeSerialization.Serialize(this, meta, false) // this will call toJSON again, but with baseOnly=true, that's why we set isThis to false.
}

/**
* Deserializes the material from JSON.
* Textures should be loaded and in meta.textures before calling this method.
* todo - needs to be tested
* @param data
* @param meta
* @param _internal
*/
fromJSON(data: any, meta?: SerializationMetaType, _internal = false): this | null {
if (_internal) {
ThreeSerialization.Deserialize(data, this, meta, true)
return this.setValues(data)
}
this.dispatchEvent({type: 'beforeDeserialize', data, meta, bubbleToObject: true, bubbleToParent: true})
return this
}

// endregion

// region UI Config

// todo dispose ui config
uiConfig: UiObjectConfig = {
type: 'folder',
label: 'Unlit Line Material',
uuid: 'MBLM2_' + this.uuid,
expanded: true,
children: [
{
type: 'input',
property: [this, 'name'],
},
// {
// type: 'monitor',
// property: [this, 'uuid'],
// },
{
type: 'checkbox',
property: [this, 'vertexColors'],
},
{
type: 'color',
property: [this, 'color'],
},
makeSamplerUi(this, 'map'),
{
type: 'number',
property: [this, 'linewidth'],
},
{
type: 'dropdown',
property: [this, 'linecap'],
children: ['butt', 'round', 'square'].map(label => ({label})),
},
{
type: 'dropdown',
property: [this, 'linejoin'],
children: ['bevel', 'round', 'miter'].map(label => ({label})),
},
// {
// type: 'checkbox',
// property: [this, 'fog'],
// },
iMaterialUI.blending(this),
iMaterialUI.polygonOffset(this),
...iMaterialUI.misc(this),
],
}

// endregion UI Config


// Class properties can also be listed with annotations like @serialize or @property
// used for serialization
static readonly MaterialProperties = {
...threeMaterialPropList,

color: new Color(0xffffff),
map: null,
linewidth: 1,
linecap: 'round',
linejoin: 'round',
fog: true,
}

static MaterialTemplate: IMaterialTemplate<UnlitLineMaterial, Partial<typeof UnlitLineMaterial.MaterialProperties>> = {
materialType: UnlitLineMaterial.TYPE,
name: 'unlit_line',
typeSlug: UnlitLineMaterial.TypeSlug,
alias: ['line_basic', 'unlit_line', UnlitLineMaterial.TYPE, UnlitLineMaterial.TypeSlug, 'LineBasicMaterial', 'LineBasicMaterial2'],
params: {
color: new Color(1, 1, 1),
},
generator: (params) => {
return new UnlitLineMaterial(params)
},
}
}

export class LineBasicMaterial2 extends UnlitLineMaterial {
constructor(parameters?: LineBasicMaterialParameters) {
super(parameters)
console.error('LineBasicMaterial2 is deprecated, use UnlitLineMaterial instead')
}
}

+ 3
- 0
src/core/material/UnlitMaterial.ts Целия файл

IMaterialGenerator, IMaterialGenerator,
IMaterialParameters, IMaterialParameters,
IMaterialTemplate, IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial' } from '../IMaterial'
import {MaterialExtension} from '../../materials' import {MaterialExtension} from '../../materials'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils' import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
public static readonly TYPE = 'UnlitMaterial' // not using .type because it is used by three.js public static readonly TYPE = 'UnlitMaterial' // not using .type because it is used by three.js
assetType = 'material' as const assetType = 'material' as const


userData: IMaterialUserData

public readonly isUnlitMaterial = true public readonly isUnlitMaterial = true


readonly appliedMeshes: Set<IObject3D> = new Set() readonly appliedMeshes: Set<IObject3D> = new Set()

+ 1
- 1
src/core/material/iMaterialCommons.ts Целия файл

export const iMaterialCommons = { export const iMaterialCommons = {
threeMaterialPropList, threeMaterialPropList,
setDirty: function(this: IMaterial, options?: IMaterialSetDirtyOptions): void { setDirty: function(this: IMaterial, options?: IMaterialSetDirtyOptions): void {
this.needsUpdate = true
if (options?.needsUpdate !== false) this.needsUpdate = true
this.dispatchEvent({bubbleToObject: true, bubbleToParent: true, ...options, type: 'materialUpdate'}) // this sets sceneUpdate in root scene this.dispatchEvent({bubbleToObject: true, bubbleToParent: true, ...options, type: 'materialUpdate'}) // this sets sceneUpdate in root scene
this.uiConfig?.uiRefresh?.(true, 'postFrame', 1) this.uiConfig?.uiRefresh?.(true, 'postFrame', 1)
}, },

+ 1
- 1
src/core/object/IObjectUi.ts Целия файл

} : {}, } : {},
], ],
} }
if (this.isMesh && isMesh !== false) {
if ((this.isLine || this.isMesh) && isMesh !== false) {
// todo: move to make mesh ui function? // todo: move to make mesh ui function?
const ui = [ const ui = [
// morph targets // morph targets

+ 4
- 4
src/core/object/iObjectCommons.ts Целия файл

}) })
// this is called initially in Material manager from process model below, not required here... // this is called initially in Material manager from process model below, not required here...
// todo: shouldnt be called from there. maybe check if material is upgraded before // todo: shouldnt be called from there. maybe check if material is upgraded before
if (currentMaterial && !Array.isArray(currentMaterial) && !currentMaterial.assetType) {
console.error('todo: initMaterial: material not upgraded')
}
// if (currentMaterial && !Array.isArray(currentMaterial) && !currentMaterial.assetType) {
// console.error('todo: initMaterial: material not upgraded')
// }
this.material = currentMaterial this.material = currentMaterial


// Legacy // Legacy
// const mat = material?.materialObject // const mat = material?.materialObject
if (!mat) continue if (!mat) continue
if (!mat.assetType) { if (!mat.assetType) {
console.error('Material not upgraded')
console.warn('Upgrading Material', mat)
iMaterialCommons.upgradeMaterial.call(mat) iMaterialCommons.upgradeMaterial.call(mat)
} }
materials.push(mat) materials.push(mat)

+ 1
- 0
src/index.ts Целия файл

export * from './postprocessing/index' export * from './postprocessing/index'
export * from './materials/index' export * from './materials/index'
export * from './rendering/index' export * from './rendering/index'
export * from './ui/image-ui'


// testing // testing
export {_testStart, _testFinish} from './testing/testing' export {_testStart, _testFinish} from './testing/testing'

+ 10
- 5
src/materials/MaterialExtender.ts Целия файл

import {IMaterial, IMaterialUserData} from '../core' import {IMaterial, IMaterialUserData} from '../core'
import {getOrCall, objectMap} from 'ts-browser-helpers' import {getOrCall, objectMap} from 'ts-browser-helpers'
import {shaderReplaceString} from '../utils/shader-helpers'
import {Object3D, Shader, WebGLRenderer} from 'three'
import {shaderReplaceString, shaderUtils} from '../utils'
import {Object3D, Shader, ShaderChunk, WebGLRenderer} from 'three'
import {MaterialExtension} from './MaterialExtension' import {MaterialExtension} from './MaterialExtension'
import {generateUUID} from '../three/utils/misc'
import {generateUUID} from '../three'


export class MaterialExtender { export class MaterialExtender {

static {
Object.assign(ShaderChunk, shaderUtils) // for #include in the shaders
}

static VoidMain = 'void main()' static VoidMain = 'void main()'


static ApplyMaterialExtensions(material: IMaterial, shader: Shader, materialExtensions: MaterialExtension[], renderer: WebGLRenderer) { static ApplyMaterialExtensions(material: IMaterial, shader: Shader, materialExtensions: MaterialExtension[], renderer: WebGLRenderer) {
} }
} }


function updateMaterialDefines(defines: MaterialExtension['extraDefines'], material: IMaterial) {
export function updateMaterialDefines(defines: MaterialExtension['extraDefines'], material: IMaterial) {
if (!defines || !material) return if (!defines || !material) return
if (material.defines === undefined || material.defines === null) { // required for some three.js materials if (material.defines === undefined || material.defines === null) { // required for some three.js materials
material.defines = {} material.defines = {}
flag = true flag = true
} }
} else if (material.defines[key] !== val) { } else if (material.defines[key] !== val) {
material.defines[key] = val
material.defines[key] = typeof val === 'boolean' ? +val : val
flag = true flag = true
} }
} }

+ 3
- 2
src/materials/MaterialExtension.ts Целия файл



/** /**
* Extra defines to copy to material * Extra defines to copy to material
* Note: boolean are converted to 0 and 1
*/ */
extraDefines?: Record<string, ValOrFunc<number|string|undefined>>;
extraDefines?: Record<string, ValOrFunc<number|string|undefined|boolean>>;


/** /**
* Custom callback to extend/modify/replace shader code and other shader properties * Custom callback to extend/modify/replace shader code and other shader properties
* This is called once when the material extension is registered. * This is called once when the material extension is registered.
* @param material * @param material
*/ */
getUiConfig?: (material: IMaterial, refreshUi: UiObjectConfig['uiRefresh']) => UiObjectConfig | undefined
getUiConfig?: (material: IMaterial, refreshUi?: UiObjectConfig['uiRefresh']) => UiObjectConfig | undefined


/** /**
* Higher priority extensions are applied first. * Higher priority extensions are applied first.

+ 1
- 1
src/materials/index.ts Целия файл

export {MaterialExtender} from './MaterialExtender'
export {MaterialExtender, updateMaterialDefines} from './MaterialExtender'
export type {MaterialExtension, IShaderPropertiesUpdater} from './MaterialExtension' export type {MaterialExtension, IShaderPropertiesUpdater} from './MaterialExtension'

+ 3
- 3
src/three/utils/SelectionWidget.ts Целия файл

import {Group, Sphere, Vector2} from 'three' import {Group, Sphere, Vector2} from 'three'
import {LineMaterial} from 'three/examples/jsm/lines/LineMaterial.js'
import {AnyOptions} from 'ts-browser-helpers' import {AnyOptions} from 'ts-browser-helpers'
import {Box3B} from '../math/Box3B' import {Box3B} from '../math/Box3B'
import {IObject3D, IWidget} from '../../core' import {IObject3D, IWidget} from '../../core'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js' import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js' import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
import {LineMaterial2} from '../../core/material/LineMaterial2'


export class SelectionWidget extends Group implements IWidget { export class SelectionWidget extends Group implements IWidget {
isWidget = true as const isWidget = true as const
export class BoxSelectionWidget extends SelectionWidget { export class BoxSelectionWidget extends SelectionWidget {
constructor() { constructor() {
super() super()
const matLine = new LineMaterial({
const matLine = new LineMaterial2({
color: '#ff2222' as any, transparent: true, opacity: 0.9, color: '#ff2222' as any, transparent: true, opacity: 0.9,
linewidth: 5, // in pixels linewidth: 5, // in pixels
resolution: new Vector2(1024, 1024), // to be set by renderer, eventually resolution: new Vector2(1024, 1024), // to be set by renderer, eventually
const ls = new LineSegmentsGeometry() const ls = new LineSegmentsGeometry()
ls.setPositions([1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1].map(v=>v - 0.5)) ls.setPositions([1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1].map(v=>v - 0.5))


const wireframe = new LineSegments2(ls, matLine)
const wireframe = new LineSegments2(ls, matLine as any)
wireframe.computeLineDistances() wireframe.computeLineDistances()
wireframe.scale.set(1, 1, 1) wireframe.scale.set(1, 1, 1)
wireframe.visible = true wireframe.visible = true

+ 1
- 0
src/utils/index.ts Целия файл

export * from './browser-helpers' export * from './browser-helpers'
export * from './shaders'
export {windowDialogWrapper, type IDialogWrapper} from './DialogWrapper' export {windowDialogWrapper, type IDialogWrapper} from './DialogWrapper'
export {GLStatsJS} from './GLStatsJS' export {GLStatsJS} from './GLStatsJS'
export {CustomContextMenu} from './CustomContextMenu' export {CustomContextMenu} from './CustomContextMenu'

+ 7
- 0
src/utils/shaders.ts Целия файл

import simpleCameraHelpers from './shaders/simpleCameraHelpers.glsl'
import randomHelpers from './shaders/randomHelpers.glsl'
import voronoiNoise from './shaders/voronoiNoise.glsl'

export const shaderUtils = {
simpleCameraHelpers, randomHelpers, voronoiNoise,
}

+ 29
- 0
src/utils/shaders/randomHelpers.glsl Целия файл

#ifndef BASIC_RANDOM_HELPERS
#define BASIC_RANDOM_HELPERS
uniform float frameCount;

float random(float n){return fract(sin(n) * 43758.5453123);}

float random2(vec2 n,float x){n+=x;return fract(sin(dot(n.xy,vec2(12.9898, 78.233)))*43758.5453);}

float random3(vec3 v) {
v = fract(v * 443.8975);
v += dot(v, v.yzx + 19.19);
return fract((v.x + v.y) * v.z);
}

// https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Shaders/Private/Random.ush#L27
float interleavedGradientNoise(const in vec2 fragCoord, const in float seed) {
vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189);
return fract(magic.z * fract(dot(fragCoord.xy + seed * vec2(2.083, 4.867), magic.xy)));
}

vec3 hash3( vec2 p )
{
vec3 q = vec3( dot(p,vec2(127.1,311.7)),
dot(p,vec2(269.5,183.3)),
dot(p,vec2(419.2,371.9)) );
return fract(sin(q)*43758.5453);
}

#endif

+ 10
- 0
src/utils/shaders/simpleCameraHelpers.glsl Целия файл

#ifndef SIMPLE_CAMERA_HELPERS
#define SIMPLE_CAMERA_HELPERS
#ifndef USE_TRANSMISSION
uniform mat4 projectionMatrix;
#endif
vec3 viewToScreen(const in vec3 pos) {
vec4 projected = projectionMatrix * vec4(pos, 1.0);
return vec3(0.5 + 0.5 * projected.xy / projected.w, projected.w);
}
#endif

+ 36
- 0
src/utils/shaders/voronoiNoise.glsl Целия файл

#ifndef VORONOI_HELPER
#define VORONOI_HELPER

float voronoi_distance(vec2 a, vec2 b, float metric) {
return distance(a, b);
}

// Blender port of the original voronoise function
float voronoi_f1_2d(in vec2 coord, in float randomness, in float flakeClamp, in float flakeRadius, inout vec3 outColor) {
vec2 cellPosition = floor(coord);
vec2 localPosition = coord - cellPosition;

float minDistance = 8.0;
vec2 targetOffset, targetPosition;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
vec2 cellOffset = vec2(i, j);
vec2 pointPosition = cellOffset + hash3(cellPosition + cellOffset).xy * randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, 1.);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
float outDistance = minDistance;
float dist = step(flakeRadius, outDistance);
outColor = hash3(cellPosition + hash3(cellPosition + targetOffset).xy * randomness + targetOffset);
vec3 outColor1 = minDistance < flakeRadius ? outColor : vec3(0.5, 0.5, 1.);
outDistance = mix(dist, minDistance, flakeClamp);
outColor = mix(outColor1, outColor, flakeClamp);
return outDistance;
}

#endif

Loading…
Отказ
Запис