Skip to content

Commit 4bd8889

Browse files
authored
Added color string support (#3931)
* Allow hex strings for color factors * added docs, updated example
1 parent 61435b8 commit 4bd8889

File tree

6 files changed

+50
-66
lines changed

6 files changed

+50
-66
lines changed

packages/model-viewer/src/features/scene-graph/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export declare interface Material {
102102
readonly emissiveTexture: TextureInfo|null;
103103

104104
readonly emissiveFactor: Readonly<RGB>;
105-
setEmissiveFactor(rgb: RGB): void;
105+
setEmissiveFactor(rgb: RGB|string): void;
106106
setAlphaCutoff(cutoff: number): void;
107107
getAlphaCutoff(): number;
108108
setDoubleSided(doubleSided: boolean): void;
@@ -180,7 +180,7 @@ export declare interface PBRMetallicRoughness {
180180
/**
181181
* Changes the base color factor of the material to the given value.
182182
*/
183-
setBaseColorFactor(rgba: RGBA): void;
183+
setBaseColorFactor(rgba: RGBA|string): void;
184184

185185
/**
186186
* Changes the metalness factor of the material to the given value.

packages/model-viewer/src/features/scene-graph/material.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16-
import {DoubleSide, FrontSide, MeshStandardMaterial} from 'three';
16+
import {Color, DoubleSide, FrontSide, MeshStandardMaterial} from 'three';
1717

1818
import {AlphaMode, GLTF, Material as GLTFMaterial, RGB} from '../../three-components/gltf-instance/gltf-2.0.js';
1919
import {Material as DefaultedMaterial} from '../../three-components/gltf-instance/gltf-defaulted.js';
@@ -252,13 +252,20 @@ export class Material extends ThreeDOMElement implements MaterialInterface {
252252
return variantData != null && this[$variantSet].has(variantData.index);
253253
}
254254

255-
setEmissiveFactor(rgb: RGB) {
255+
setEmissiveFactor(rgb: RGB|string) {
256256
this[$ensureMaterialIsLoaded]();
257+
const color = new Color();
258+
if (rgb instanceof Array) {
259+
color.fromArray(rgb);
260+
} else {
261+
color.set(rgb).convertSRGBToLinear();
262+
}
257263
for (const material of this[$correlatedObjects] as
258264
Set<MeshStandardMaterial>) {
259-
material.emissive.fromArray(rgb);
265+
material.emissive.set(color);
260266
}
261-
(this[$sourceObject] as DefaultedMaterial).emissiveFactor = rgb;
267+
(this[$sourceObject] as DefaultedMaterial).emissiveFactor =
268+
color.toArray() as [number, number, number];
262269
this[$onUpdate]();
263270
}
264271

packages/model-viewer/src/features/scene-graph/pbr-metallic-roughness.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16-
import {MeshStandardMaterial} from 'three';
16+
import {Color, MeshStandardMaterial} from 'three';
1717

1818
import {GLTF, PBRMetallicRoughness as GLTFPBRMetallicRoughness} from '../../three-components/gltf-instance/gltf-2.0.js';
1919
import {PBRMetallicRoughness as DefaultedPBRMetallicRoughness} from '../../three-components/gltf-instance/gltf-defaulted.js';
@@ -105,14 +105,25 @@ export class PBRMetallicRoughness extends ThreeDOMElement implements
105105
return this[$metallicRoughnessTexture];
106106
}
107107

108-
setBaseColorFactor(rgba: RGBA) {
108+
setBaseColorFactor(rgba: RGBA|string) {
109+
const color = new Color();
110+
if (rgba instanceof Array) {
111+
color.fromArray(rgba);
112+
} else {
113+
color.set(rgba).convertSRGBToLinear();
114+
}
109115
for (const material of this[$threeMaterials]) {
110-
material.color.fromArray(rgba);
111-
material.opacity = (rgba)[3];
116+
material.color.set(color);
117+
if (rgba instanceof Array) {
118+
material.opacity = (rgba)[3];
119+
} else {
120+
rgba = [0, 0, 0, material.opacity];
121+
color.toArray(rgba);
122+
}
112123
}
113124
const pbrMetallicRoughness =
114125
this[$sourceObject] as DefaultedPBRMetallicRoughness;
115-
pbrMetallicRoughness.baseColorFactor = rgba;
126+
pbrMetallicRoughness.baseColorFactor = rgba as RGBA;
116127
this[$onUpdate]();
117128
}
118129

packages/modelviewer.dev/examples/scenegraph/index.html

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,11 @@ <h2 class="demo-title">Model Transformations</h2>
207207
<h4 id="intro"><span class="font-medium">Directly manipulate the scene graph</span></h4>
208208
<div class="heading">
209209
<h2 class="demo-title">Change Material Base Color</h2>
210-
<p>This is an experimental feature, and the API is considered highly
211-
unstable. Please try it out, but be prepared for it to change!</p>
210+
<p>Note that color factors can be set two ways: [r, g, b, a] or a
211+
CSS-style color string. For array input, the values are between 0
212+
and 1 and represent a linear color space, identical to the glTF
213+
spec. For string inputs, the values are internally converted from
214+
the sRGB color space, so this is likely more user-friendly.</p>
212215
<p>As above, you can change these values in AR, but only in WebXR
213216
mode. iOS Quick Look does not reflect these color changes as USDZ
214217
does not appear to support colors multiplied onto textures.</p>
@@ -217,27 +220,18 @@ <h2 class="demo-title">Change Material Base Color</h2>
217220
<template>
218221
<model-viewer id="color" camera-controls touch-action="pan-y" interaction-prompt="none" src="../../shared-assets/models/Astronaut.glb" ar alt="A 3D model of an astronaut">
219222
<div class="controls", id="color-controls">
220-
<button data-color="1,0,0,1">Red</button>
221-
<button data-color="0,1,0,1">Green</button>
222-
<button data-color="0,0,1,1">Blue</button>
223+
<button data-color="#ff0000">Red</button>
224+
<button data-color="#00ff00">Green</button>
225+
<button data-color="#0000ff">Blue</button>
223226
</div>
224227
</model-viewer>
225228
<script>
226229
const modelViewerColor = document.querySelector("model-viewer#color");
227230

228231
document.querySelector('#color-controls').addEventListener('click', (event) => {
229232
const colorString = event.target.dataset.color;
230-
231-
if (!colorString) {
232-
return;
233-
}
234-
235-
const color = colorString.split(',')
236-
.map(numberString => parseFloat(numberString));
237-
238-
console.log('Changing color to: ', color);
239233
const [material] = modelViewerColor.model.materials;
240-
material.pbrMetallicRoughness.setBaseColorFactor(color);
234+
material.pbrMetallicRoughness.setBaseColorFactor(colorString);
241235
});
242236
</script>
243237
</template>
@@ -533,7 +527,7 @@ <h2 class="demo-title">Materials API</h2>
533527
readonly emissiveFactor: RGB;
534528
readonly pbrMetallicRoughness: PBRMetallicRoughness;
535529

536-
setEmissiveFactor(rgb: RGB): void;
530+
setEmissiveFactor(rgb: RGB|string): void;
537531
setAlphaCutoff(cutoff: number): void;
538532
getAlphaCutoff(): number;
539533
setDoubleSided(doubleSided: boolean): void;
@@ -549,7 +543,7 @@ <h2 class="demo-title">Materials API</h2>
549543
readonly baseColorTexture: TextureInfo|null;
550544
readonly metallicRoughnessTexture: TextureInfo|null;
551545

552-
setBaseColorFactor(rgba: RGBA): void;
546+
setBaseColorFactor(rgba: RGBA|string): void;
553547
setMetallicFactor(value: number): void;
554548
setRoughnessFactor(value: number): void;
555549
}

packages/space-opera/src/components/materials_panel/materials_panel.ts

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,6 @@ export class MaterialPanel extends ConnectedLitElement {
229229
1.055 * (Math.pow(val, 0.41666)) - 0.055;
230230
}
231231

232-
srgbToLinear(val: number): number {
233-
return (val < 0.04045) ? val * 0.0773993808 :
234-
Math.pow(val * 0.9478672986 + 0.0521327014, 2.4);
235-
}
236-
237232
/* Interpolate base color as curr approaches duration */
238233
getInterpolatedColor(original: RGBA, curr: number, duration: number): RGBA {
239234
const INTERP_COLOR = [0, 0, 0];
@@ -262,8 +257,9 @@ export class MaterialPanel extends ConnectedLitElement {
262257
// Logic for interpolating from red emissive factor to the original.
263258
interpolateMaterial() {
264259
this.isInterpolating = true;
265-
const originalBaseColor = this.selectedBaseColor;
266-
const originalEmissiveFactor = this.selectedEmissiveFactor;
260+
const originalBaseColor =
261+
this.getMaterial().pbrMetallicRoughness.baseColorFactor;
262+
const originalEmissiveFactor = this.getMaterial().emissiveFactor;
267263

268264
let start = -1;
269265
const DURATION = 500; // in milliseconds
@@ -613,32 +609,6 @@ export class MaterialPanel extends ConnectedLitElement {
613609
}
614610
};
615611

616-
get selectedBaseColor(): RGBA {
617-
const alphaFactor =
618-
this.getMaterial().pbrMetallicRoughness.baseColorFactor[3];
619-
const selectedColor = color.hexToRgb(this.baseColorPicker.selectedColorHex);
620-
// color.hexToRgb returns RGB vals from 0-255, but glTF expects a val from
621-
// 0-1.
622-
return [
623-
this.srgbToLinear(selectedColor[0] / 255),
624-
this.srgbToLinear(selectedColor[1] / 255),
625-
this.srgbToLinear(selectedColor[2] / 255),
626-
alphaFactor
627-
];
628-
}
629-
630-
get selectedEmissiveFactor(): RGB {
631-
const selectedColor =
632-
color.hexToRgb(this.emissiveFactorPicker.selectedColorHex);
633-
// color.hexToRgb returns RGB vals from 0-255, but glTF expects a val from
634-
// 0-1.
635-
return [
636-
this.srgbToLinear(selectedColor[0] / 255),
637-
this.srgbToLinear(selectedColor[1] / 255),
638-
this.srgbToLinear(selectedColor[2] / 255)
639-
];
640-
}
641-
642612
get selectedRoughnessFactor(): number {
643613
return checkFinite(Number(this.roughnessFactorSlider.value));
644614
}
@@ -689,7 +659,8 @@ export class MaterialPanel extends ConnectedLitElement {
689659
onBaseColorChange() {
690660
const material = this.getMaterialVariant();
691661

692-
material.pbrMetallicRoughness.setBaseColorFactor(this.selectedBaseColor);
662+
material.pbrMetallicRoughness.setBaseColorFactor(
663+
this.baseColorPicker.selectedColorHex);
693664

694665
reduxStore.dispatch(dispatchModelDirty());
695666
}
@@ -815,7 +786,8 @@ export class MaterialPanel extends ConnectedLitElement {
815786
}
816787

817788
onEmissiveFactorChanged() {
818-
this.getMaterialVariant().setEmissiveFactor(this.selectedEmissiveFactor);
789+
this.getMaterialVariant().setEmissiveFactor(
790+
this.emissiveFactorPicker.selectedColorHex);
819791
reduxStore.dispatch(dispatchModelDirty());
820792
}
821793

@@ -850,7 +822,7 @@ export class MaterialPanel extends ConnectedLitElement {
850822

851823
onAlphaFactorChange() {
852824
const material = this.getMaterialVariant();
853-
const rgba = this.selectedBaseColor;
825+
const rgba = this.getMaterial().pbrMetallicRoughness.baseColorFactor;
854826
rgba[3] = this.alphaFactorSlider.value;
855827
material.pbrMetallicRoughness.setBaseColorFactor(rgba);
856828
reduxStore.dispatch(dispatchModelDirty());

packages/space-opera/src/test/materials_panel/materials_panel_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ describe('material panel test', () => {
7979
async () => {
8080
panel.selectedMaterialIndex = 0;
8181
await panel.updateComplete;
82-
expect(panel.selectedBaseColor).toEqual([1, 0, 1, 1]);
82+
expect(panel.baseColorPicker.selectedColorHex).toEqual('#ff00ff');
8383
expect(panel.selectedRoughnessFactor).toEqual(0.2);
8484
expect(panel.selectedMetallicFactor).toEqual(1);
8585

8686
panel.selectedMaterialIndex = 1;
8787
await panel.updateComplete;
88-
expect(panel.selectedBaseColor).toEqual([1, 1, 0, 1]);
88+
expect(panel.baseColorPicker.selectedColorHex).toEqual('#ffff00');
8989
expect(panel.selectedRoughnessFactor).toEqual(1);
9090
expect(panel.selectedMetallicFactor).toEqual(1);
9191
});

0 commit comments

Comments
 (0)