Skip to content

Commit e5a7285

Browse files
committed
feat(core): support pierced property
1 parent 37f58cd commit e5a7285

File tree

3 files changed

+50
-31
lines changed

3 files changed

+50
-31
lines changed

apps/examples/src/app/soba/lod/scene.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ interface BustGLTF extends GLTF {
2828
receiveShadow
2929
[geometry]="level.nodes.Mesh_0001.geometry"
3030
[material]="level.materials.default"
31-
>
32-
<ngt-value [rawValue]="0.25" attach="material.envMapIntensity" />
33-
</ngt-mesh>
31+
[material.envMapIntensity]="0.75"
32+
/>
3433
}
3534
</ngts-detailed>
3635
`,

libs/core/src/lib/renderer/renderer.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -470,22 +470,23 @@ export class NgtRenderer2 implements Renderer2 {
470470
const instanceState = getInstanceState(el);
471471
if (instanceState) instanceState.attach = paths;
472472
}
473+
return;
474+
}
475+
476+
// coercion for primitive values
477+
let maybeCoerced: string | number | boolean = value;
478+
479+
if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
480+
maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
473481
} else {
474-
// coercion for primitive values
475-
let maybeCoerced: string | number | boolean = value;
476-
477-
if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
478-
maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
479-
} else {
480-
const maybeNumber = Number(maybeCoerced);
481-
if (!isNaN(maybeNumber)) maybeCoerced = maybeNumber;
482-
}
482+
const maybeNumber = Number(maybeCoerced);
483+
if (!isNaN(maybeNumber)) maybeCoerced = maybeNumber;
484+
}
483485

484-
if (name === 'rawValue') {
485-
rS[NgtRendererClassId.rawValue] = maybeCoerced;
486-
} else {
487-
applyProps(el, { [name]: maybeCoerced });
488-
}
486+
if (name === 'rawValue') {
487+
rS[NgtRendererClassId.rawValue] = maybeCoerced;
488+
} else {
489+
applyProps(el, { [name]: maybeCoerced });
489490
}
490491

491492
return;

libs/core/src/lib/utils/apply-props.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { checkUpdate } from './update';
66

77
// This function prepares a set of changes to be applied to the instance
88
function diffProps(instance: NgtAnyRecord, props: NgtAnyRecord) {
9-
const propsEntries = Object.entries(props);
109
const changes: [key: string, value: unknown][] = [];
1110

12-
for (const [propKey, propValue] of propsEntries) {
11+
for (const propKey in props) {
12+
const propValue = props[propKey];
1313
let key = propKey;
1414
if (is.colorSpaceExist(instance)) {
1515
if (propKey === 'encoding') {
@@ -50,6 +50,21 @@ function getMemoizedPrototype(root: any) {
5050
return ctor;
5151
}
5252

53+
function resolve(instance: any, key: string): { root: any; targetKey: string; targetProp: any } {
54+
let targetProp = instance[key];
55+
if (!key.includes('.')) return { root: instance, targetKey: key, targetProp };
56+
57+
// Resolve pierced target
58+
const chain = key.split('.');
59+
targetProp = chain.reduce((acc, part) => acc[part], instance);
60+
const targetKey = chain.pop()!;
61+
62+
// Switch root if atomic
63+
if (!targetProp?.set) instance = chain.reduce((acc, part) => acc[part], instance);
64+
65+
return { root: instance, targetKey, targetProp };
66+
}
67+
5368
// This function applies a set of changes to the instance
5469
export function applyProps<T extends NgtAnyRecord>(instance: NgtInstanceState<T>['object'], props: NgtAnyRecord) {
5570
// if props is empty
@@ -83,8 +98,12 @@ export function applyProps<T extends NgtAnyRecord>(instance: NgtInstanceState<T>
8398
// }
8499
// }
85100

86-
const currentInstance = instance;
87-
const targetProp = (currentInstance as NgtAnyRecord)[key];
101+
const { root, targetKey, targetProp } = resolve(instance, key);
102+
103+
// we have switched due to pierced props
104+
if (root !== instance) {
105+
return applyProps(root, { [targetKey]: value });
106+
}
88107

89108
// Copy if properties match signatures
90109
if (
@@ -97,13 +116,13 @@ export function applyProps<T extends NgtAnyRecord>(instance: NgtInstanceState<T>
97116
is.three<THREE.BufferGeometry>(targetProp, 'isBufferGeometry') &&
98117
is.three<THREE.BufferGeometry>(value, 'isBufferGeometry')
99118
) {
100-
Object.assign(currentInstance, { [key]: value });
119+
Object.assign(root, { [targetKey]: value });
101120
} else {
102121
// fetch the default state of the target
103-
const ctor = getMemoizedPrototype(currentInstance);
122+
const ctor = getMemoizedPrototype(root);
104123
// The target key was originally null or undefined, which indicates that the object which
105124
// is now present was externally set by the user, we should therefore assign the value directly
106-
if (ctor !== undefined && ctor[key] == null) Object.assign(currentInstance, { [key]: value });
125+
if (ctor !== undefined && ctor[targetKey] == null) Object.assign(root, { [targetKey]: value });
107126
// Otherwise copy is correct
108127
else targetProp.copy(value);
109128
}
@@ -127,26 +146,26 @@ export function applyProps<T extends NgtAnyRecord>(instance: NgtInstanceState<T>
127146
}
128147
// Else, just overwrite the value
129148
else {
130-
Object.assign(currentInstance, { [key]: value });
149+
Object.assign(root, { [targetKey]: value });
131150

132151
// Auto-convert sRGB texture parameters for built-in materials
133152
// https://github.com/pmndrs/react-three-fiber/issues/344
134153
// https://github.com/mrdoob/three.js/pull/25857
135154
if (
136155
rootState &&
137156
!rootState.linear &&
138-
colorMaps.includes(key) &&
139-
(currentInstance[key] as THREE.Texture | undefined)?.isTexture &&
157+
colorMaps.includes(targetKey) &&
158+
(root[targetKey] as THREE.Texture | undefined)?.isTexture &&
140159
// sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
141-
currentInstance[key].format === THREE.RGBAFormat &&
142-
currentInstance[key].type === THREE.UnsignedByteType
160+
root[targetKey].format === THREE.RGBAFormat &&
161+
root[targetKey].type === THREE.UnsignedByteType
143162
) {
144163
// NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
145-
currentInstance[key].colorSpace = THREE.SRGBColorSpace;
164+
root[targetKey].colorSpace = THREE.SRGBColorSpace;
146165
}
147166
}
148167

149-
checkUpdate(currentInstance[key]);
168+
checkUpdate(root[targetKey]);
150169
checkUpdate(targetProp);
151170
invalidateInstance(instance as NgtInstanceNode<T>);
152171
}

0 commit comments

Comments
 (0)