Просмотр исходного кода

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 лет назад
Родитель
Сommit
4e524efcf0
Аккаунт пользователя с таким Email не найден

+ 0
- 1
src/assetmanager/AssetManager.ts Просмотреть файл

@@ -303,7 +303,6 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}
if (!data.metadata) {
console.warn('Invalid data(no metadata)', data)
}
console.log(data, event)
if (event.material) {
if (data.metadata?.type !== 'Material') {
console.warn('Invalid material data', data)

+ 6
- 1
src/assetmanager/MaterialManager.ts Просмотреть файл

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

/**
* Material Manager
@@ -26,6 +28,8 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> {
readonly templates: IMaterialTemplate[] = [
PhysicalMaterial.MaterialTemplate,
UnlitMaterial.MaterialTemplate,
UnlitLineMaterial.MaterialTemplate,
LineMaterial2.MaterialTemplate,
LegacyPhongMaterial.MaterialTemplate,
]

@@ -334,5 +338,6 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> {
}
return applied
}

}


+ 1
- 1
src/core/IMaterial.ts Просмотреть файл

@@ -58,7 +58,7 @@ export interface IMaterialUserData extends IImportResultUserData{

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

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


// legacy, to be removed

+ 1
- 0
src/core/IObject.ts Просмотреть файл

@@ -153,6 +153,7 @@ export interface IObject3D<E extends Event = IObject3DEvent, ET = IObject3DEvent
isCamera?: boolean
isMesh?: boolean
isLine?: boolean
isLineSegments?: boolean
// isGroup?: boolean
isScene?: boolean
// isHelper?: boolean

+ 2
- 0
src/core/index.ts Просмотреть файл

@@ -4,6 +4,8 @@ export {ExtendedShaderMaterial} from './material/ExtendedShaderMaterial'
export {PhysicalMaterial, type PhysicalMaterialEventTypes, MeshStandardMaterial2} from './material/PhysicalMaterial'
export {ShaderMaterial2} from './material/ShaderMaterial2'
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 {iObjectCommons} from './object/iObjectCommons'
export {iCameraCommons} from './object/iCameraCommons'

+ 3
- 0
src/core/material/ExtendedShaderMaterial.ts Просмотреть файл

@@ -14,12 +14,15 @@ import {
WebGLRenderer,
} from 'three'
import {shaderReplaceString} from '../../utils'
import {IMaterialUserData} from '../IMaterial'

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

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

userData: IMaterialUserData

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

+ 18
- 6
src/core/material/IMaterialUi.ts Просмотреть файл

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

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

export const iMaterialUI = {
base: (material: IMaterial): UiObjectConfig[] => [
@@ -129,11 +136,11 @@ export const iMaterialUI = {
value: value[1],
})),
},
{
material.alphaMap !== undefined ? {
type: 'image',
property: [material, 'alphaMap'],
},
makeSamplerUi(material, 'alphaMap'),
} : {},
material.alphaMap !== undefined ? makeSamplerUi(material, 'alphaMap') : {},
{
type: 'checkbox',
label: 'Render to Gbuffer',
@@ -211,6 +218,12 @@ export const iMaterialUI = {
}
),
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',
label: 'Side',
@@ -250,7 +263,6 @@ export const iMaterialUI = {
})
},
},
()=>material.materialExtensions?.map(v=>v.getUiConfig?.(material, material.uiConfig?.uiRefresh)).filter(v=>v),
],
roughMetal: (material: PhysicalMaterial): UiObjectConfig => (
{
@@ -287,7 +299,7 @@ export const iMaterialUI = {
children: [
{
type: 'slider',
bounds: [0, 0.2],
bounds: [-0.2, 0.2],
stepSize: 0.001,
property: [material, 'bumpScale'],
hidden: ()=>!material.bumpMap,

+ 3
- 0
src/core/material/LegacyPhongMaterial.ts Просмотреть файл

@@ -17,6 +17,7 @@ import {
IMaterialGenerator,
IMaterialParameters,
IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial'
import {MaterialExtension} from '../../materials'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
@@ -35,6 +36,8 @@ export class LegacyPhongMaterial extends MeshPhongMaterial<IMaterialEvent, Phong
public static readonly TYPE = 'LegacyPhongMaterial' // not using .type because it is used by three.js
assetType = 'material' as const

userData: IMaterialUserData

public readonly isLegacyPhongMaterial = true

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

+ 210
- 0
src/core/material/LineMaterial2.ts Просмотреть файл

@@ -0,0 +1,210 @@
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 Просмотреть файл

@@ -14,7 +14,7 @@ import {
Vector2,
WebGLRenderer,
} from 'three'
import {shaderReplaceString} from '../../utils/shader-helpers'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
import {
IMaterial,
IMaterialEvent,
@@ -22,8 +22,8 @@ import {
IMaterialGenerator,
IMaterialParameters,
IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial'
import {SerializationMetaType, ThreeSerialization} from '../../utils/serialization'
import {MaterialExtension} from '../../materials'
import {iMaterialCommons, threeMaterialPropList} from './iMaterialCommons'
import {IObject3D} from '../IObject'
@@ -38,6 +38,8 @@ export class PhysicalMaterial extends MeshPhysicalMaterial<IMaterialEvent, Physi
public static readonly TYPE = 'PhysicalMaterial' // not using .type because it is used by three.js
assetType = 'material' as const

userData: IMaterialUserData

public readonly isPhysicalMaterial = true

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

+ 4
- 1
src/core/material/ShaderMaterial2.ts Просмотреть файл

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

@@ -38,6 +38,9 @@ export class ShaderMaterial2<E extends IMaterialEvent = IMaterialEvent, ET = IMa
}

assetType = 'material' as const

userData: IMaterialUserData

public readonly isAShaderMaterial = true

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

+ 227
- 0
src/core/material/UnlitLineMaterial.ts Просмотреть файл

@@ -0,0 +1,227 @@
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 Просмотреть файл

@@ -16,6 +16,7 @@ import {
IMaterialGenerator,
IMaterialParameters,
IMaterialTemplate,
IMaterialUserData,
} from '../IMaterial'
import {MaterialExtension} from '../../materials'
import {SerializationMetaType, shaderReplaceString, ThreeSerialization} from '../../utils'
@@ -33,6 +34,8 @@ export class UnlitMaterial extends MeshBasicMaterial<IMaterialEvent, UnlitMateri
public static readonly TYPE = 'UnlitMaterial' // not using .type because it is used by three.js
assetType = 'material' as const

userData: IMaterialUserData

public readonly isUnlitMaterial = true

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

+ 1
- 1
src/core/material/iMaterialCommons.ts Просмотреть файл

@@ -75,7 +75,7 @@ export const threeMaterialPropList = {
export const iMaterialCommons = {
threeMaterialPropList,
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.uiConfig?.uiRefresh?.(true, 'postFrame', 1)
},

+ 1
- 1
src/core/object/IObjectUi.ts Просмотреть файл

@@ -174,7 +174,7 @@ export function makeIObject3DUiConfig(this: IObject3D, isMesh?:boolean): UiObjec
} : {},
],
}
if (this.isMesh && isMesh !== false) {
if ((this.isLine || this.isMesh) && isMesh !== false) {
// todo: move to make mesh ui function?
const ui = [
// morph targets

+ 4
- 4
src/core/object/iObjectCommons.ts Просмотреть файл

@@ -117,9 +117,9 @@ export const iObjectCommons = {
})
// 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
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

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

+ 1
- 0
src/index.ts Просмотреть файл

@@ -8,6 +8,7 @@ export * from './plugins/index'
export * from './postprocessing/index'
export * from './materials/index'
export * from './rendering/index'
export * from './ui/image-ui'

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

+ 10
- 5
src/materials/MaterialExtender.ts Просмотреть файл

@@ -1,11 +1,16 @@
import {IMaterial, IMaterialUserData} from '../core'
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 {generateUUID} from '../three/utils/misc'
import {generateUUID} from '../three'

export class MaterialExtender {

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

static VoidMain = 'void main()'

static ApplyMaterialExtensions(material: IMaterial, shader: Shader, materialExtensions: MaterialExtension[], renderer: WebGLRenderer) {
@@ -96,7 +101,7 @@ export class MaterialExtender {
}
}

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

+ 3
- 2
src/materials/MaterialExtension.ts Просмотреть файл

@@ -15,8 +15,9 @@ export interface MaterialExtension{

/**
* 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
@@ -85,7 +86,7 @@ export interface MaterialExtension{
* This is called once when the material extension is registered.
* @param material
*/
getUiConfig?: (material: IMaterial, refreshUi: UiObjectConfig['uiRefresh']) => UiObjectConfig | undefined
getUiConfig?: (material: IMaterial, refreshUi?: UiObjectConfig['uiRefresh']) => UiObjectConfig | undefined

/**
* Higher priority extensions are applied first.

+ 1
- 1
src/materials/index.ts Просмотреть файл

@@ -1,2 +1,2 @@
export {MaterialExtender} from './MaterialExtender'
export {MaterialExtender, updateMaterialDefines} from './MaterialExtender'
export type {MaterialExtension, IShaderPropertiesUpdater} from './MaterialExtension'

+ 3
- 3
src/three/utils/SelectionWidget.ts Просмотреть файл

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

export class SelectionWidget extends Group implements IWidget {
isWidget = true as const
@@ -79,7 +79,7 @@ export class SelectionWidget extends Group implements IWidget {
export class BoxSelectionWidget extends SelectionWidget {
constructor() {
super()
const matLine = new LineMaterial({
const matLine = new LineMaterial2({
color: '#ff2222' as any, transparent: true, opacity: 0.9,
linewidth: 5, // in pixels
resolution: new Vector2(1024, 1024), // to be set by renderer, eventually
@@ -90,7 +90,7 @@ export class BoxSelectionWidget extends SelectionWidget {
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))

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

+ 1
- 0
src/utils/index.ts Просмотреть файл

@@ -1,4 +1,5 @@
export * from './browser-helpers'
export * from './shaders'
export {windowDialogWrapper, type IDialogWrapper} from './DialogWrapper'
export {GLStatsJS} from './GLStatsJS'
export {CustomContextMenu} from './CustomContextMenu'

+ 7
- 0
src/utils/shaders.ts Просмотреть файл

@@ -0,0 +1,7 @@
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 Просмотреть файл

@@ -0,0 +1,29 @@
#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 Просмотреть файл

@@ -0,0 +1,10 @@
#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 Просмотреть файл

@@ -0,0 +1,36 @@
#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

Загрузка…
Отмена
Сохранить