Przeglądaj źródła

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 lat temu
rodzic
commit
4e524efcf0
No account linked to committer's email address

+ 0
- 1
src/assetmanager/AssetManager.ts Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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 Wyświetl plik

@@ -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

Ładowanie…
Anuluj
Zapisz