control_Lighting.ts

import {Control, IControlParams} from "./Control";
import {Dialog} from '../ui/Dialog';
import {Layer} from "../layer/Layer";
import {Slider} from "../ui/Slider";
import {Sun} from "./Sun";
import {ToggleButton} from "../ui/ToggleButton";
import {View} from '../ui/View';
import {Atmosphere} from "./Atmosphere";
import {Color} from "../ui/Color";
import {SimpleSkyBackground} from "../control/SimpleSkyBackground";

interface ILightingParams extends IControlParams {

}

const SUN_STOP_SVG_ICON = `<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 70.41" style="enable-background:new 0 0 122.88 70.41" xml:space="preserve"><g><path d="M60.91,19.12c6.95,0,13.24,2.95,17.8,7.72c4.55,4.77,7.37,11.37,7.37,18.64c0,1.94-0.2,3.83-0.58,5.65h31.61 c2.1,0,2.62,1.16,2.62,2.59c0,1.43-0.52,2.59-2.62,2.59H7.09c-2.1,0-2.62-1.16-2.62-2.59c0-1.43,0.52-2.59,2.62-2.59h29.23 c-0.38-1.82-0.58-3.71-0.58-5.65c0-7.28,2.82-13.87,7.37-18.64C47.67,22.08,53.96,19.12,60.91,19.12L60.91,19.12L60.91,19.12z M63.4,70.41c-2.1,0-2.62-1.16-2.62-2.59s0.52-2.59,2.62-2.59h56.86c2.1,0,2.62,1.16,2.62,2.59s-0.52,2.59-2.62,2.59H63.4 L63.4,70.41z M2.62,70.41c-2.1,0-2.62-1.16-2.62-2.59s0.52-2.59,2.62-2.59h29.51c2.1,0,2.62,1.16,2.62,2.59s-0.52,2.59-2.62,2.59 H2.62L2.62,70.41z M38.39,9.46c-0.78-1.35-0.32-3.07,1.03-3.85c1.35-0.78,3.07-0.32,3.85,1.03l3.62,6.27 c0.78,1.35,0.32,3.07-1.03,3.85c-1.35,0.78-3.07,0.32-3.85-1.03L38.39,9.46L38.39,9.46L38.39,9.46z M58.67,2.83 c0-1.56,1.27-2.83,2.83-2.83c1.56,0,2.83,1.27,2.83,2.83v7.24c0,1.56-1.27,2.83-2.83,2.83c-1.56,0-2.83-1.26-2.83-2.83V2.83 L58.67,2.83L58.67,2.83z M79.56,7.23c0.77-1.35,2.49-1.81,3.84-1.04c1.35,0.77,1.81,2.49,1.04,3.84l-3.62,6.27 c-0.77,1.35-2.49,1.81-3.84,1.04c-1.35-0.77-1.81-2.49-1.04-3.84L79.56,7.23L79.56,7.23L79.56,7.23z M95.45,21.48 c1.35-0.78,3.07-0.32,3.85,1.03c0.78,1.35,0.32,3.07-1.03,3.85L92,29.98c-1.35,0.78-3.07,0.32-3.85-1.03 c-0.78-1.35-0.32-3.07,1.03-3.85L95.45,21.48L95.45,21.48L95.45,21.48z M102.08,41.76c1.56,0,2.83,1.27,2.83,2.83 c0,1.56-1.27,2.83-2.83,2.83h-7.24c-1.56,0-2.83-1.26-2.83-2.83s1.26-2.83,2.83-2.83H102.08L102.08,41.76L102.08,41.76z M19.74,46.25c-1.56,0-2.83-1.27-2.83-2.83c0-1.56,1.27-2.83,2.83-2.83h7.24c1.56,0,2.83,1.26,2.83,2.83s-1.27,2.83-2.83,2.83 H19.74L19.74,46.25L19.74,46.25z M24.14,25.35c-1.35-0.77-1.81-2.49-1.04-3.84c0.77-1.35,2.49-1.81,3.84-1.04l6.27,3.62 c1.35,0.77,1.81,2.49,1.04,3.84c-0.77,1.35-2.49,1.81-3.84,1.04L24.14,25.35L24.14,25.35L24.14,25.35z"/></g></svg>`;
const SUN_ACTIVE_SVG_ICON = `<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
    <path style="text-indent:0;text-align:start;line-height:normal;text-transform:none;block-progression:tb;-inkscape-font-specification:Sans" d="M 16 4 C 9.3844277 4 4 9.3844277 4 16 C 4 22.615572 9.3844277 28 16 28 C 22.615572 28 28 22.615572 28 16 C 28 9.3844277 22.615572 4 16 4 z M 16 6 C 16.389823 6 16.778223 6.0506339 17.15625 6.09375 C 18.631659 7.6568432 21 10.9245 21 16 C 21 21.0755 18.631659 24.343157 17.15625 25.90625 C 16.778223 25.949366 16.389823 26 16 26 C 10.465308 26 6 21.534692 6 16 C 6 10.465308 10.465308 6 16 6 z" overflow="visible" font-family="Sans"/>
</svg>
`;
const LIGHTING_ENABLED_SVG_ICON = `<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by IcoMoon.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 512 512">
<g>
</g>
\t<path d="M257.894 156.948c-53.258 0-96.42 43.561-96.42 97.26 0 53.719 43.161 97.249 96.42 97.249 53.197 0 96.379-43.53 96.379-97.25 0-53.698-43.182-97.26-96.379-97.26zM257.894 309.873c-30.464 0-55.163-24.945-55.163-55.665s24.699-55.624 55.163-55.624c30.423 0 55.101 24.904 55.101 55.624s-24.688 55.665-55.101 55.665z" fill="#000000" />
\t<path d="M241.808 43.499h32.144v79.575h-32.144v-79.575z" fill="#000000" />
\t<path d="M417.209 115.897l-22.723-22.917-55.757 56.279 22.723 22.907z" fill="#000000" />
\t<path d="M389.468 238.407h78.899v32.389h-78.899v-32.389z" fill="#000000" />
\t<path d="M396.012 416.86l22.723-22.917-55.767-56.259-22.712 22.897z" fill="#000000" />
\t<path d="M242.473 388.915h32.144v79.575h-32.144v-79.575z" fill="#000000" />
\t<path d="M96.266 396.073l22.722 22.938 55.778-56.289-22.692-22.928z" fill="#000000" />
\t<path d="M43.633 240.537h78.879v32.43h-78.879v-32.43z" fill="#000000" />
\t<path d="M115.394 93.051l-22.681 22.907 55.757 56.258 22.702-22.917z" fill="#000000" />
</svg>`;
const ATMOSPHERE_SVG_ICON = `<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path fill="#000000" d="M135.688 18.5c-6.798 74.842-23.842 85.39-107.907 59.656 84.85 52.022 73.57 64.954-6.843 96.938 87.743-10.27 103.29 4.89 70.75 87.594 17.805-27.56 32.5-44.498 46.282-54.47-11.813 28.26-18.345 59.274-18.345 91.813 0 84.184 43.71 157.96 109.656 200.376-41.624-43.834-67.686-102.7-67.686-167.875 0-134.923 109.45-244.405 244.375-244.405 30.92 0 60.76 5.762 88 16.25-38.584-26.87-85.517-42.625-136.064-42.625-55.257 0-106.14 18.802-146.562 50.375 4.627-18.783 17.39-38.073 41.03-60.906C190.18 90.942 153.53 95.634 135.69 18.5zm10.03 77.188c5.67.002 11.428 1.247 16.876 3.874 14.506 6.998 22.72 21.81 22 36.938-10.26 10.87-19.507 22.696-27.594 35.344-9.035 2.753-19.075 2.27-28.25-2.156-19.37-9.343-27.5-32.6-18.156-51.97 6.715-13.92 20.638-22.036 35.125-22.03z"/></svg>`;


const TEMPLATE =
    `<div class="og-lighing og-options-container">

         <div class="og-option">
           <div class="og-suncontrol"></div>
         </div>        
         
         <div class="og-option og-atmosphere-opacity">
         </div>
         
         <div class="og-option og-simpleskybackground">
         </div>
         
        <div class="og-lighting-emptyline"></div>

         <div class="og-option og-gamma"></div>         
         <div class="og-option og-exposure"></div>
       
        <div class="og-lighting-emptyline"></div>

         <div class="og-option">
         <div class="og-layers">
           <div class="og-caption">Select layer:</div>
           <select id="layers"></select>
         </div>
         </div>

         <div class="og-option og-opacity">
         </div>
         
         <div class="og-option og-night">
         </div>
         
         <div class="og-lighting-emptyline"></div>

         <div class="og-option og-diffuse">
         </div>
      
         <div class="og-option og-ambient">
         </div>

         <div class="og-option og-specular">
         </div>        

    </div>`;

const ICON_BUTTON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">

<defs>
</defs>
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
\t<path d="M 45 68 c -12.682 0 -23 -10.317 -23 -23 c 0 -12.682 10.318 -23 23 -23 c 12.683 0 23 10.318 23 23 C 68 57.683 57.683 68 45 68 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 45 17.556 c -1.657 0 -3 -1.343 -3 -3 V 3 c 0 -1.657 1.343 -3 3 -3 c 1.657 0 3 1.343 3 3 v 11.556 C 48 16.212 46.657 17.556 45 17.556 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 45 90 c -1.657 0 -3 -1.343 -3 -3 V 75.444 c 0 -1.657 1.343 -3 3 -3 c 1.657 0 3 1.343 3 3 V 87 C 48 88.657 46.657 90 45 90 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 14.556 48 H 3 c -1.657 0 -3 -1.343 -3 -3 c 0 -1.657 1.343 -3 3 -3 h 11.556 c 1.657 0 3 1.343 3 3 C 17.556 46.657 16.212 48 14.556 48 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 87 48 H 75.444 c -1.657 0 -3 -1.343 -3 -3 c 0 -1.657 1.343 -3 3 -3 H 87 c 1.657 0 3 1.343 3 3 C 90 46.657 88.657 48 87 48 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 66.527 26.473 c -0.768 0 -1.535 -0.293 -2.121 -0.878 c -1.172 -1.172 -1.172 -3.071 0 -4.243 l 8.171 -8.171 c 1.172 -1.172 3.07 -1.171 4.242 0 c 1.172 1.172 1.172 3.071 0 4.243 l -8.171 8.171 C 68.063 26.18 67.295 26.473 66.527 26.473 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 15.302 77.698 c -0.768 0 -1.536 -0.293 -2.121 -0.879 c -1.172 -1.171 -1.172 -3.071 0 -4.242 l 8.171 -8.171 c 1.171 -1.172 3.071 -1.172 4.242 0 c 1.172 1.171 1.172 3.071 0 4.242 l -8.171 8.171 C 16.837 77.405 16.069 77.698 15.302 77.698 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 23.473 26.473 c -0.768 0 -1.536 -0.293 -2.121 -0.878 l -8.171 -8.171 c -1.172 -1.172 -1.172 -3.071 0 -4.243 c 1.172 -1.172 3.072 -1.171 4.243 0 l 8.171 8.171 c 1.172 1.172 1.172 3.071 0 4.243 C 25.008 26.18 24.24 26.473 23.473 26.473 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
\t<path d="M 74.698 77.698 c -0.768 0 -1.535 -0.293 -2.121 -0.879 l -8.171 -8.171 c -1.172 -1.171 -1.172 -3.071 0 -4.242 c 1.172 -1.172 3.07 -1.172 4.242 0 l 8.171 8.171 c 1.172 1.171 1.172 3.071 0 4.242 C 76.233 77.405 75.466 77.698 74.698 77.698 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
</g>
</svg>`;

const MAX_COLOR = 5;

/**
 * Helps to set up lighting.
 */
export class Lighting extends Control {
    protected _selectedLayer: Layer | null;
    protected _toggleBtn: ToggleButton;
    protected _dialog: Dialog<null>;
    protected _panel: View<null>;

    protected _atmosphereMaxOpacity: Slider;
    protected _atmosphereMinOpacity: Slider;

    protected _simpleSkyBackgroundColorOne: Color;
    protected _simpleSkyBackgroundColorTwo: Color;

    protected _gamma: Slider;
    protected _exposure: Slider;
    protected _night: Slider;
    protected _opacity: Slider;
    protected _diffuse_r: Slider;
    protected _diffuse_g: Slider;
    protected _diffuse_b: Slider;
    protected _ambient_r: Slider;
    protected _ambient_g: Slider;
    protected _ambient_b: Slider;
    protected _specular_r: Slider;
    protected _specular_g: Slider;
    protected _specular_b: Slider;
    protected _shininess: Slider;

    public $gamma: HTMLElement | null;
    public $exposure: HTMLElement | null;
    public $night: HTMLElement | null;
    public $opacity: HTMLElement | null;
    public $diffuse: HTMLElement | null;
    public $ambient: HTMLElement | null;
    public $specular: HTMLElement | null;
    public $atmosphereOpacity: HTMLElement | null;
    public $simpleSkyBackground: HTMLElement | null;


    constructor(options: ILightingParams = {}) {
        super(options);

        this._selectedLayer = null;

        this._toggleBtn = new ToggleButton({
            classList: ["og-map-button", "og-lighting_button"],
            icon: ICON_BUTTON_SVG
        });

        this._dialog = new Dialog({
            title: "Lighting Parameters",
            visible: false,
            useHide: true,
            top: 60,
            left: 60,
            width: 600
        });

        this._dialog.events.on("visibility", (v: boolean) => {
            this._toggleBtn.setActive(v);
        });

        this._panel = new View({
            template: TEMPLATE
        });

        this.$gamma = null;
        this.$exposure = null;
        this.$night = null;
        this.$opacity = null;
        this.$diffuse = null;
        this.$ambient = null;
        this.$specular = null;
        this.$atmosphereOpacity = null;
        this.$simpleSkyBackground = null;

        this._atmosphereMaxOpacity = new Slider({
            label: "Max.opacity",
            max: 5
        });

        this._atmosphereMinOpacity = new Slider({
            label: "Min.opacity",
            max: 5
        });

        this._simpleSkyBackgroundColorOne = new Color({
            label: "Color One"
        });

        this._simpleSkyBackgroundColorTwo = new Color({
            label: "Color Two"
        });

        this._gamma = new Slider({
            label: "Gamma",
            max: 5
        });

        this._exposure = new Slider({
            label: "Exposure",
            max: 5
        });

        this._night = new Slider({
            label: "Nightlight",
            max: 5
        });

        this._opacity = new Slider({
            label: "Opacity",
            max: 1
        });

        //
        // Diffuse sliders
        //
        this._diffuse_r = new Slider({
            label: "Diffuse R",
            max: MAX_COLOR
        });

        this._diffuse_g = new Slider({
            label: "Diffuse G",
            max: MAX_COLOR
        });

        this._diffuse_b = new Slider({
            label: "Diffuse B",
            max: MAX_COLOR
        });

        //
        // Ambient sliders
        //
        this._ambient_r = new Slider({
            label: "Ambient R",
            max: MAX_COLOR
        });

        this._ambient_g = new Slider({
            label: "Ambient G",
            max: MAX_COLOR
        });

        this._ambient_b = new Slider({
            label: "Ambient B",
            max: MAX_COLOR
        });

        //
        // Specular sliders
        //
        this._specular_r = new Slider({
            label: "Specular R",
            max: 0.2
        });

        this._specular_g = new Slider({
            label: "Specular G",
            max: 0.2
        });

        this._specular_b = new Slider({
            label: "Specular B",
            max: 0.2
        });

        this._shininess = new Slider({
            label: "Shininess",
            max: 100
        });
    }

    public bindLayer(layer: Layer) {
        this._selectedLayer = layer;
        this._opacity.value = layer.opacity;
        this._update();
    }

    public override oninit() {

        this._toggleBtn.appendTo(this.renderer!.div!);
        this._dialog.appendTo(this.renderer!.div!);
        this._panel.appendTo(this._dialog.container!);

        if (this._panel.el) {
            this.$atmosphereOpacity = this._panel.el.querySelector(".og-atmosphere-opacity");
            this.$simpleSkyBackground = this._panel.el.querySelector(".og-simpleskybackground");
            this.$gamma = this._panel.el.querySelector(".og-option.og-gamma");
            this.$exposure = this._panel.el.querySelector(".og-option.og-exposure");
            this.$opacity = this._panel.el.querySelector(".og-option.og-opacity");
            this.$diffuse = this._panel.el.querySelector(".og-option.og-diffuse");
            this.$ambient = this._panel.el.querySelector(".og-option.og-ambient");
            this.$specular = this._panel.el.querySelector(".og-option.og-specular");
            this.$night = this._panel.el.querySelector(".og-option.og-night");
        }

        this._toggleBtn.events.on("change", (isActive: boolean) => {
            this._dialog.setVisibility(isActive);
        });

        let $suncontrol = this._dialog.select(".og-suncontrol")!;

        let sunStopBtn = new ToggleButton({
            classList: ["og-suncontrol-button"],
            isActive: true,
            icon: SUN_STOP_SVG_ICON,
            title: "Star/stop the Sun from following the camera"
        });
        sunStopBtn.appendTo($suncontrol);

        let sunActiveBtn = new ToggleButton({
            classList: ["og-suncontrol-button"],
            isActive: true,
            icon: SUN_ACTIVE_SVG_ICON,
            title: "Activate/deactivate the Sun current time positioning"
        });
        sunActiveBtn.appendTo($suncontrol);

        sunStopBtn.events.on("change", (isActive: boolean) => {
            const sun = this.planet!.renderer!.controls.sun as Sun;
            if (isActive) {
                sun.start();
            } else {
                sun.stop();
            }
        });

        sunActiveBtn.events.on("change", (isActive: boolean) => {
            const sun = this.planet!.renderer!.controls.sun;
            if (isActive) {
                sun.activate();
            } else {
                sun.deactivate();
            }
        });

        let lightingEnabledBtn = new ToggleButton({
            classList: ["og-suncontrol-button"],
            isActive: this.planet!.lightEnabled,
            icon: LIGHTING_ENABLED_SVG_ICON,
            title: "Enable/disable planet lighting"
        });
        lightingEnabledBtn.appendTo($suncontrol);

        lightingEnabledBtn.events.on("change", (isActive: boolean) => {
            this.planet!.lightEnabled = isActive;
        });

        let atmosphereEnabledBtn = new ToggleButton({
            classList: ["og-suncontrol-button"],
            isActive: this.planet!.atmosphereEnabled,
            icon: ATMOSPHERE_SVG_ICON,
            title: "Enable/disable atmosphere scattering"
        });
        atmosphereEnabledBtn.appendTo($suncontrol);

        if (this.planet!.atmosphereEnabled) {
            this.$atmosphereOpacity!.style.display = "block";
            this.$simpleSkyBackground!.style.display = "none";
        } else {
            this.$atmosphereOpacity!.style.display = "none";
            this.$simpleSkyBackground!.style.display = "flex";
        }

        atmosphereEnabledBtn.events.on("change", (isActive: boolean) => {
            this.planet!.atmosphereEnabled = isActive;
            if (this.planet!.atmosphereEnabled) {
                this.$atmosphereOpacity!.style.display = "block";
                this.$simpleSkyBackground!.style.display = "none";
            } else {
                this.$atmosphereOpacity!.style.display = "none";
                this.$simpleSkyBackground!.style.display = "flex";
            }
        });

        this._atmosphereMaxOpacity.appendTo(this.$atmosphereOpacity!);
        this._atmosphereMinOpacity.appendTo(this.$atmosphereOpacity!);

        this._simpleSkyBackgroundColorOne.appendTo(this.$simpleSkyBackground!);
        this._simpleSkyBackgroundColorTwo.appendTo(this.$simpleSkyBackground!);

        this._gamma.appendTo(this.$gamma!);
        this._exposure.appendTo(this.$exposure!);

        this._night.appendTo(this.$night!);
        this._opacity.appendTo(this.$opacity!);

        this._diffuse_r.appendTo(this.$diffuse!);
        this._diffuse_g.appendTo(this.$diffuse!);
        this._diffuse_b.appendTo(this.$diffuse!);

        this._ambient_r.appendTo(this.$ambient!);
        this._ambient_g.appendTo(this.$ambient!);
        this._ambient_b.appendTo(this.$ambient!);

        this._specular_r.appendTo(this.$specular!);
        this._specular_g.appendTo(this.$specular!);
        this._specular_b.appendTo(this.$specular!);
        this._shininess.appendTo(this.$specular!);

        //
        // Screen options
        //
        this._gamma.value = this.planet!.renderer!.gamma;
        this._gamma.events.on("change", (val: number) => {
            this.planet!.renderer!.gamma = val;
        });

        this._exposure.value = this.planet!.renderer!.exposure;
        this._exposure.events.on("change", (val: number) => {
            this.planet!.renderer!.exposure = val;
        });

        //
        // Atmosphere parameters
        //
        this._atmosphereMinOpacity.value = this.planet!.atmosphereMinOpacity;
        this._atmosphereMinOpacity.events.on("change", (val: number) => {
            this.planet!.atmosphereMinOpacity = val;
        });

        this._atmosphereMaxOpacity.value = this.planet!.atmosphereMaxOpacity;
        this._atmosphereMaxOpacity.events.on("change", (val: number) => {
            this.planet!.atmosphereMaxOpacity = val;
            let atmos = this.planet!.renderer!.controls.Atmosphere as Atmosphere;
            atmos.opacity = val;
        });

        //
        // Simple Sky Background parameters
        //
        let simpleSkyBackgroundControl = this.planet!.renderer!.controls.SimpleSkyBackground as SimpleSkyBackground;
        if (simpleSkyBackgroundControl) {
            this._simpleSkyBackgroundColorOne.value = simpleSkyBackgroundControl.colorOne;
            this._simpleSkyBackgroundColorTwo.value = simpleSkyBackgroundControl.colorTwo;
        }

        this._simpleSkyBackgroundColorOne.events.on("input", (val: string) => {
            let simpleSkyBackgroundControl = this.planet!.renderer!.controls.SimpleSkyBackground as SimpleSkyBackground;
            if (simpleSkyBackgroundControl) {
                simpleSkyBackgroundControl.colorOne = val;
            }
        });

        this._simpleSkyBackgroundColorTwo.events.on("input", (val: string) => {
            let simpleSkyBackgroundControl = this.planet!.renderer!.controls.SimpleSkyBackground as SimpleSkyBackground;
            if (simpleSkyBackgroundControl) {
                simpleSkyBackgroundControl.colorTwo = val;
            }
        });

        //
        // Planet options
        //
        this._panel.el!.querySelector<HTMLSelectElement>("#layers")!.addEventListener("change", (e: Event) => {
            const l = this.planet!.getLayerByName((e.target as HTMLSelectElement).value);
            if (l) {
                this.bindLayer(l);
            }
        });

        this._night.events.on("change", (val: number) => {
            if (this._selectedLayer) {
                this._selectedLayer.nightTextureCoefficient = val;
            }
        });

        this._opacity.events.on("change", (val: number) => {
            if (this._selectedLayer)
                this._selectedLayer.opacity = val;
        });

        this._ambient_r.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._ambient)
                this._selectedLayer._ambient[0] = val
        });

        this._ambient_g.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._ambient)
                this._selectedLayer._ambient[1] = val
        });

        this._ambient_b.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._ambient)
                this._selectedLayer._ambient[2] = val
        });

        this._diffuse_r.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._diffuse)
                this._selectedLayer._diffuse[0] = val
        });

        this._diffuse_g.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._diffuse)
                this._selectedLayer._diffuse[1] = val
        });

        this._diffuse_b.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._diffuse)
                this._selectedLayer._diffuse[2] = val
        });

        this._specular_r.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._specular)
                this._selectedLayer._specular[0] = val
        });

        this._specular_g.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._specular)
                this._selectedLayer._specular[1] = val
        });

        this._specular_b.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._specular)
                this._selectedLayer._specular[2] = val
        });

        this._shininess.events.on("change", (val: number) => {
            if (this._selectedLayer && this._selectedLayer._specular)
                this._selectedLayer._specular[3] = val
        });


        if (this.planet) {
            this.planet!.events.on("layeradd", this._onLayerAdd, this);
            this.planet!.events.on("layerremove", this._onLayerRemove, this);
        }

        this._fetchLayers();
    }

    protected _update() {
        let l = this._selectedLayer;

        this._opacity.value = l && l.opacity ? l.opacity : 0.0;

        this._night.value = l && l.nightTextureCoefficient ? l.nightTextureCoefficient : this.planet!.nightTextureCoefficient;

        let a = l && l._ambient ? l._ambient : this.planet!._ambient;
        this._ambient_r.value = a[0];
        this._ambient_g.value = a[1];
        this._ambient_b.value = a[2];

        let d = l && l._diffuse ? l._diffuse : this.planet!._diffuse;
        this._diffuse_r.value = d[0];
        this._diffuse_g.value = d[1];
        this._diffuse_b.value = d[2];

        let s = l && l._specular ? l._specular : this.planet!._specular;
        this._specular_r.value = s[0];
        this._specular_g.value = s[1];
        this._specular_b.value = s[2];
        this._shininess.value = s[3];
    }

    protected _fetchLayers() {
        if (this.planet) {
            for (let i = 0; i < this.planet.layers.length; i++) {
                this._onLayerAdd(this.planet.layers[i]);
            }
        }
    }

    protected _onLayerAdd(e: Layer) {
        this.bindLayer(e);
        let opt = document.createElement("option");
        opt.value = e.name;
        opt.innerText = e.name;
        this._panel.el!.querySelector("#layers")!.appendChild(opt);
        this._panel.el!.querySelector<HTMLSelectElement>("#layers")!.value = e.name;
    }

    protected _onLayerRemove(e: Layer) {
    }
}