407 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// Celestial body constructor
 | 
						||
var CelestialBody = function (obj) {
 | 
						||
    // Meta
 | 
						||
    this.name = "";
 | 
						||
    // If the planet is the sun
 | 
						||
    this.star = false;
 | 
						||
    // Object shape info
 | 
						||
    this.spherical = true;
 | 
						||
    this.oblateness = 0.;
 | 
						||
    this.radius = 1.;
 | 
						||
    this.isComet = false;
 | 
						||
    this.particleSystem = null;
 | 
						||
    // Parent/moon objects
 | 
						||
    this.parent = null;
 | 
						||
    this.children = [];
 | 
						||
    // TODO: Model info, to be implemented
 | 
						||
    // Orbit parameters
 | 
						||
    // 周期(恒星)、半长轴、离心率、倾角、升交点黄经、平近点角 (历时原点假设轨道是圆形时的黄经偏移)
 | 
						||
 | 
						||
    this.position = {
 | 
						||
        x: 0, y: 0, z: 0,
 | 
						||
    };
 | 
						||
 | 
						||
    this.obj = {
 | 
						||
        path: null, objPath: null, mtlPath: null,
 | 
						||
        scale: 1., angle: 0., x: 0., y: 0., z: 0.
 | 
						||
    };
 | 
						||
 | 
						||
    this.orbit = {
 | 
						||
        period: 1., semiMajorAxis: 1., eccentricity: 0.,
 | 
						||
        inclination: 0., ascendingNode: 0., meanLongitude: 0.
 | 
						||
    };
 | 
						||
    // Rotation parameters
 | 
						||
    // 周期(恒星)、倾角(黄赤夹角)、子午角(自转轴所在的与黄道垂直的平面,即子午面,与xOy平面的夹角)、历时原点角度偏移
 | 
						||
    // 注:这里我们使用xOz平面作为黄道面
 | 
						||
    this.rotation = {
 | 
						||
        period: 1., inclination: 1.,
 | 
						||
        meridianAngle: 0., offset: 0.
 | 
						||
    };
 | 
						||
    // 远景时显示光芒的参数设定
 | 
						||
    // albedo 为反照率
 | 
						||
    // 下面给出一个该把这个光点画多亮的粗略估计(只是用来看的,不是很严谨)
 | 
						||
    // x > R/k:   (2 - <c, p>/(|c|*|p|)) * R^2 * a * log(k*x0/R) / log(k*x/R)
 | 
						||
    // else:    0
 | 
						||
    // 其中,a是反照率,记号<,>表示内积,|.|是二范数,c是摄像机坐标,p是天体坐标
 | 
						||
    // R 是天体半径,x 是距天体的距离,即|c - p|,k 是一个系数
 | 
						||
    this.albedo = 1.;
 | 
						||
    this.shineColor = 0xffffff;
 | 
						||
    // Material settings
 | 
						||
    this.material = {
 | 
						||
        // "phong", "lambert", "basic"
 | 
						||
        type: "phong",
 | 
						||
        diffuse: {map: null, color: 0xffffff},
 | 
						||
        specular: {map: null, color: 0xffffff, shininess: 25},
 | 
						||
        night: {map: null},
 | 
						||
        bump: {map: null, height: 10}
 | 
						||
    };
 | 
						||
    // Planet ring definitions
 | 
						||
    this.ring = {
 | 
						||
        map: null,
 | 
						||
        lower: 2000, higher: 6000,
 | 
						||
        color: 0xffffff, specularColor: 0xffffff, specularPower: 5
 | 
						||
    };
 | 
						||
    // halo effect
 | 
						||
    this.halo = {
 | 
						||
        color: null,
 | 
						||
        radius: 1.
 | 
						||
    };
 | 
						||
    this.atmosphere = {
 | 
						||
        cloud: {
 | 
						||
            map: null, height: 1, speed: 20
 | 
						||
        },
 | 
						||
        // By wave length
 | 
						||
        scattering: false,
 | 
						||
        atmosphereColor: new THREE.Vector3(0.5, 0.7, 0.8),
 | 
						||
        sunsetColor: new THREE.Vector3(0.8, 0.7, 0.6),
 | 
						||
        atmosphereStrength: 1.0,
 | 
						||
        sunsetStrength: 1.0
 | 
						||
    };
 | 
						||
 | 
						||
    mergeRecursive(this, obj);
 | 
						||
};
 | 
						||
 | 
						||
function mergeRecursive(obj1, obj2) {
 | 
						||
    for (var p in obj2) {
 | 
						||
        try {
 | 
						||
            //Property in destination object set; update its value.
 | 
						||
            if (obj2[p].constructor == Object) {
 | 
						||
                obj1[p] = mergeRecursive(obj1[p], obj2[p]);
 | 
						||
            } else {
 | 
						||
                obj1[p] = obj2[p];
 | 
						||
            }
 | 
						||
        } catch (e) {
 | 
						||
            //Property in destination object not set; create it and set its value.
 | 
						||
            obj1[p] = obj2[p];
 | 
						||
        }
 | 
						||
    }
 | 
						||
    return obj1;
 | 
						||
}
 | 
						||
 | 
						||
// lens flare texture
 | 
						||
CelestialBody.prototype.flareTexture = textureLoader.load("res/effects/flare.jpg");
 | 
						||
 | 
						||
// IMPORTANT: This function of the prototype generate the object and put it on
 | 
						||
// the scene. This is the most most important part in drawing the object.
 | 
						||
CelestialBody.prototype.generateObjectsOnScene = function (argScene) {
 | 
						||
    var that = this;
 | 
						||
    // if(this.spherical)
 | 
						||
    if (!this.spherical) {
 | 
						||
        if (this.isComet) {
 | 
						||
            this.cometPivot = new THREE.Group();
 | 
						||
            this.objectGroup = new THREE.Group();
 | 
						||
            this.particleSystem = new THREE.GPUParticleSystem({
 | 
						||
                maxParticles: 150000
 | 
						||
            });
 | 
						||
            this.objectGroup.add(this.particleSystem);
 | 
						||
            argScene.add(this.objectGroup);
 | 
						||
        } else {
 | 
						||
            this.objectGroup = new THREE.Group();
 | 
						||
            var onProgress = function (xhr) {
 | 
						||
                if (xhr.lengthComputable) {
 | 
						||
                    var percentComplete = xhr.loaded / xhr.total * 100;
 | 
						||
                }
 | 
						||
            };
 | 
						||
            var onError = function (xhr) {
 | 
						||
            };
 | 
						||
            if (that.obj.mtlPath != null) {
 | 
						||
                mtlLoader.setPath(that.obj.path);
 | 
						||
                mtlLoader.load(that.obj.mtlPath, function (materials) {
 | 
						||
                    materials.preload();
 | 
						||
                    objLoader.setMaterials(materials);
 | 
						||
                    objLoader.setPath(that.obj.path);
 | 
						||
                    objLoader.load(that.obj.objPath, function (object) {
 | 
						||
                        that.objectGroup.add(object);
 | 
						||
                        var scale = that.obj.scale;
 | 
						||
                        object.rotateY(that.obj.angle / 180.0 * Math.PI);
 | 
						||
                        object.scale.set(scale, scale, scale);
 | 
						||
                        object.translateX(that.obj.x);
 | 
						||
                        object.translateY(that.obj.y);
 | 
						||
                        object.translateZ(that.obj.z);
 | 
						||
                    }, onProgress, onError);
 | 
						||
                });
 | 
						||
            } else {
 | 
						||
                objLoader.setPath(that.obj.path);
 | 
						||
                objLoader.load(that.obj.objPath, function (object) {
 | 
						||
                    object.traverse(function (child) {
 | 
						||
                        var material = new THREE.MeshLambertMaterial();
 | 
						||
                        if (child instanceof THREE.Mesh) {
 | 
						||
                            child.material = material;
 | 
						||
                        }
 | 
						||
                    });
 | 
						||
                    that.objectGroup.add(object);
 | 
						||
                    object.rotateY(that.obj.angle / 180.0 * Math.PI);
 | 
						||
                    var scale = that.obj.scale;
 | 
						||
                    object.scale.set(scale, scale, scale);
 | 
						||
                    object.translateX(that.obj.x);
 | 
						||
                    object.translateY(that.obj.y);
 | 
						||
                    object.translateZ(that.obj.z);
 | 
						||
                }, onProgress, onError);
 | 
						||
            }
 | 
						||
            argScene.add(this.objectGroup);
 | 
						||
        }
 | 
						||
    } else {
 | 
						||
        this.bodySphereGeometry = new THREE.SphereGeometry(this.radius, 64, 64);
 | 
						||
        // else if(!this.spherical) blablabla...
 | 
						||
        // The base body sphere material
 | 
						||
        var sphereMaterial = this.bodySphereMaterial = null;
 | 
						||
        switch (this.material.type) {
 | 
						||
            case "basic":
 | 
						||
                sphereMaterial = this.bodySphereMaterial
 | 
						||
                    = new THREE.MeshBasicMaterial({
 | 
						||
                    color: new THREE.Color(this.material.diffuse.color)
 | 
						||
                });
 | 
						||
                if (this.material.diffuse.map !== null) {
 | 
						||
                    sphereMaterial.map = textureLoader.load(this.material.diffuse.map);
 | 
						||
                }
 | 
						||
                break;
 | 
						||
            case "lambert":
 | 
						||
                sphereMaterial = this.bodySphereMaterial
 | 
						||
                    = new THREE.MeshPhongMaterial({
 | 
						||
                    color: new THREE.Color(this.material.diffuse.color),
 | 
						||
                    specular: new THREE.Color(0x000000),
 | 
						||
                    shininess: 0,
 | 
						||
                    bumpScale: this.material.bump.height
 | 
						||
                });
 | 
						||
                if (this.material.diffuse.map !== null) {
 | 
						||
                    sphereMaterial.map = textureLoader.load(this.material.diffuse.map);
 | 
						||
                }
 | 
						||
                break;
 | 
						||
            case "phong":
 | 
						||
            default:
 | 
						||
                sphereMaterial = this.bodySphereMaterial
 | 
						||
                    = new THREE.MeshPhongMaterial({
 | 
						||
                    color: new THREE.Color(this.material.diffuse.color),
 | 
						||
                    specular: new THREE.Color(this.material.specular.color),
 | 
						||
                    shininess: this.material.specular.shininess,
 | 
						||
                    bumpScale: this.material.bump.height
 | 
						||
                });
 | 
						||
                if (this.material.diffuse.map !== null) {
 | 
						||
                    sphereMaterial.map = textureLoader.load(this.material.diffuse.map);
 | 
						||
                }
 | 
						||
                if (this.material.specular.map !== null) {
 | 
						||
                    sphereMaterial.specularMap = textureLoader.load(this.material.specular.map);
 | 
						||
                }
 | 
						||
                if (this.material.bump.map !== null) {
 | 
						||
                    sphereMaterial.bumpMap = textureLoader.load(this.material.bump.map);
 | 
						||
                }
 | 
						||
                break;
 | 
						||
        }
 | 
						||
        this.objectGroup = new THREE.Group();
 | 
						||
        // Add the main body part
 | 
						||
        textureLoader.load(this.material.diffuse.map, function (texture) {
 | 
						||
            this.bodySphereMaterial = new THREE.MeshPhongMaterial({map: texture});
 | 
						||
        });
 | 
						||
        this.bodySphereMesh = new THREE.Mesh(this.bodySphereGeometry, this.bodySphereMaterial);
 | 
						||
        this.bodySphereMesh.scale.set(1, 1 - this.oblateness, 1);
 | 
						||
 | 
						||
        // Add lens flare
 | 
						||
        this.lensFlare = null;
 | 
						||
        if (this.star) {
 | 
						||
            this.lensFlare =
 | 
						||
                new THREE.LensFlare(this.flareTexture, 200,
 | 
						||
                    0, THREE.AdditiveBlending, new THREE.Color(this.shineColor));
 | 
						||
            this.lensFlare.position.set(this.getX(), this.getY(), this.getZ());
 | 
						||
 | 
						||
            var that = this;
 | 
						||
            this.lensFlare.customUpdateCallback = function () {
 | 
						||
                var cameraDistance = Math.sqrt(
 | 
						||
                    (trackCamera[params.Camera].getX() - that.getX())
 | 
						||
                    * (trackCamera[params.Camera].getX() - that.getX()),
 | 
						||
                    (trackCamera[params.Camera].getY() - that.getY())
 | 
						||
                    * (trackCamera[params.Camera].getY() - that.getY()),
 | 
						||
                    (trackCamera[params.Camera].getZ() - that.getZ())
 | 
						||
                    * (trackCamera[params.Camera].getZ() - that.getZ()));
 | 
						||
                this.transparent = 0.3;
 | 
						||
                if (cameraDistance < 6000) {
 | 
						||
                    that.bodySphereMaterial.depthTest = true;
 | 
						||
                    that.haloMaterial.depthTest = true;
 | 
						||
                    that.cloudMaterial.depthTest = true;
 | 
						||
                }
 | 
						||
                else {
 | 
						||
                    that.bodySphereMaterial.depthTest = false;
 | 
						||
                    that.haloMaterial.depthTest = false;
 | 
						||
                }
 | 
						||
                this.updateLensFlares();
 | 
						||
            };
 | 
						||
        }
 | 
						||
 | 
						||
        // Add night
 | 
						||
        this.nightMaterial = null;
 | 
						||
        this.nightSphereMesh = null;
 | 
						||
        if (this.material.night.map !== null) {
 | 
						||
            this.nightMaterial = new THREE.ShaderMaterial({
 | 
						||
                uniforms: {
 | 
						||
                    nightTexture: {value: textureLoader.load(this.material.night.map)}
 | 
						||
                },
 | 
						||
                vertexShader: generalVS,
 | 
						||
                fragmentShader: nightFS,
 | 
						||
                transparent: true,
 | 
						||
                blending: THREE.CustomBlending,
 | 
						||
                blendEquation: THREE.AddEquation
 | 
						||
            });
 | 
						||
            this.nightSphereMesh = new THREE.Mesh(this.bodySphereGeometry, this.nightMaterial);
 | 
						||
            this.objectGroup.add(this.nightSphereMesh);
 | 
						||
        }
 | 
						||
 | 
						||
        // Add clouds
 | 
						||
        this.cloudGeometry = null;
 | 
						||
        this.cloudMaterial = null;
 | 
						||
        this.cloudMesh = null;
 | 
						||
        if (this.atmosphere.cloud.map !== null) {
 | 
						||
            this.cloudGeometry = new THREE.SphereGeometry(this.radius + this.atmosphere.cloud.height, 64, 64);
 | 
						||
            if (!this.star) {
 | 
						||
                this.cloudMaterial = new THREE.MeshLambertMaterial({
 | 
						||
                    map: textureLoader.load(this.atmosphere.cloud.map),
 | 
						||
                    transparent: true
 | 
						||
                });
 | 
						||
            } else {
 | 
						||
                this.cloudMaterial = new THREE.MeshBasicMaterial({
 | 
						||
                    map: textureLoader.load(this.atmosphere.cloud.map),
 | 
						||
                    transparent: true
 | 
						||
                });
 | 
						||
            }
 | 
						||
            this.cloudMesh = new THREE.Mesh(this.cloudGeometry, this.cloudMaterial);
 | 
						||
        }
 | 
						||
 | 
						||
        // Add atmosphere
 | 
						||
        this.atmosphereGeometry = null;
 | 
						||
        this.atmosphereMaterial = null;
 | 
						||
        this.atmosphereMesh = null;
 | 
						||
        if (this.atmosphere.scattering) {
 | 
						||
            this.atmosphereGeometry = new THREE.SphereGeometry(this.radius * 1.015, 64, 64);
 | 
						||
            this.atmosphereMaterial = new THREE.ShaderMaterial({
 | 
						||
                uniforms: {
 | 
						||
                    atmosphereColor: {value: this.atmosphere.atmosphereColor},
 | 
						||
                    sunsetColor: {value: this.atmosphere.sunsetColor},
 | 
						||
                    atmosphereStrength: {value: this.atmosphere.atmosphereStrength},
 | 
						||
                    sunsetStrength: {value: this.atmosphere.sunsetStrength}
 | 
						||
                },
 | 
						||
                vertexShader: atmosphereVS,
 | 
						||
                fragmentShader: atmosphereFS,
 | 
						||
                transparent: true,
 | 
						||
                blending: THREE.CustomBlending,
 | 
						||
                blendEquation: THREE.AddEquation
 | 
						||
            });
 | 
						||
            this.atmosphereMesh = new THREE.Mesh(this.atmosphereGeometry, this.atmosphereMaterial);
 | 
						||
            this.objectGroup.add(this.atmosphereMesh);
 | 
						||
        }
 | 
						||
 | 
						||
        this.haloGeometry = null;
 | 
						||
        this.haloMaterial = null;
 | 
						||
        this.haloMesh = null;
 | 
						||
        if (this.halo.color != null) {
 | 
						||
            this.haloGeometry = new THREE.SphereGeometry(this.halo.radius, 64, 64);
 | 
						||
            this.haloMaterial = new THREE.ShaderMaterial({
 | 
						||
                uniforms: {
 | 
						||
                    color: {value: this.halo.color}
 | 
						||
                },
 | 
						||
                vertexShader: haloVS,
 | 
						||
                fragmentShader: haloFS,
 | 
						||
                transparent: true,
 | 
						||
                blending: THREE.CustomBlending,
 | 
						||
                blendEquation: THREE.AddEquation
 | 
						||
            });
 | 
						||
            this.haloMesh = new THREE.Mesh(this.haloGeometry, this.haloMaterial);
 | 
						||
            this.objectGroup.add(this.haloMesh);
 | 
						||
        }
 | 
						||
 | 
						||
        // Add rings
 | 
						||
        // Add clouds
 | 
						||
        this.ringGeometry = null;
 | 
						||
        this.ringMaterial = null;
 | 
						||
        this.ringMeshPositive = null;
 | 
						||
        this.ringMeshNegative = null;
 | 
						||
        this.ringTexture = null;
 | 
						||
        if (this.ring.map !== null) {
 | 
						||
            this.ringTexture = textureLoader.load(this.ring.map);
 | 
						||
            this.ringTexture.rotation = Math.PI / 2;
 | 
						||
            this.ringGeometry = new THREE.CylinderGeometry(this.radius + this.ring.lower, this.radius + this.ring.higher, 0, 100, 100, true);
 | 
						||
            this.ringMaterial = new THREE.MeshPhongMaterial({
 | 
						||
                map: this.ringTexture, transparent: true,
 | 
						||
                emissive: new THREE.Color(0x222222)
 | 
						||
            });
 | 
						||
            this.ringMeshPositive = new THREE.Mesh(this.ringGeometry, this.ringMaterial);
 | 
						||
            this.ringGeometry = new THREE.CylinderGeometry(this.radius + this.ring.higher, this.radius + this.ring.lower, 0, 100, 100, true);
 | 
						||
            this.ringMeshNegative = new THREE.Mesh(this.ringGeometry, this.ringMaterial);
 | 
						||
            // if(this.name === "Saturn") {
 | 
						||
            //     this.ringMeshPositive.castShadow = true;
 | 
						||
            //     this.ringMeshPositive.receiveShadow = true;
 | 
						||
            //     this.ringMeshNegative.castShadow = true;
 | 
						||
            //     this.ringMeshNegative.receiveShadow = true;
 | 
						||
            //     this.bodySphereMesh.castShadow = true;
 | 
						||
            //     this.bodySphereMesh.receiveShadow = true;
 | 
						||
            // }
 | 
						||
        }
 | 
						||
 | 
						||
        // Add meshes to the object group
 | 
						||
        if (this.lensFlare != null) this.objectGroup.add(this.lensFlare);
 | 
						||
        this.objectGroup.add(this.bodySphereMesh);
 | 
						||
 | 
						||
        if (this.ringMeshPositive !== null) {
 | 
						||
            this.objectGroup.add(this.ringMeshPositive);
 | 
						||
            this.objectGroup.add(this.ringMeshNegative);
 | 
						||
        }
 | 
						||
        if (this.cloudMesh !== null) {
 | 
						||
            this.objectGroup.add(this.cloudMesh);
 | 
						||
        }
 | 
						||
        // simple inclination
 | 
						||
        this.objectGroup.rotateZ(this.rotation.inclination / 180.0 * Math.PI);
 | 
						||
        argScene.add(this.objectGroup);
 | 
						||
    }
 | 
						||
};
 | 
						||
 | 
						||
 | 
						||
CelestialBody.prototype.updateClouds = function (time) {
 | 
						||
    if (this.cloudGeometry !== null) {
 | 
						||
        this.cloudGeometry.rotateY(this.atmosphere.cloud.speed / 180.0 * Math.PI);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
CelestialBody.prototype.update = function (time) {
 | 
						||
    if (this.objectGroup !== undefined || this.isComet) {
 | 
						||
        this.updateOrbitAndRotation(time);
 | 
						||
        if (this.spherical && !this.isComet)
 | 
						||
            this.updateClouds(time);
 | 
						||
    }
 | 
						||
};
 | 
						||
 | 
						||
CelestialBody.prototype.getX = function () {
 | 
						||
    if (this.objectGroup == null || this.objectGroup.position == null) return 0;
 | 
						||
    return this.objectGroup.position.getComponent(0);
 | 
						||
};
 | 
						||
 | 
						||
CelestialBody.prototype.getY = function () {
 | 
						||
    if (this.objectGroup == null || this.objectGroup.position == null) return 0;
 | 
						||
    return this.objectGroup.position.getComponent(1);
 | 
						||
};
 | 
						||
 | 
						||
CelestialBody.prototype.getZ = function () {
 | 
						||
    if (this.objectGroup == null || this.objectGroup.position == null) return 0;
 | 
						||
    return this.objectGroup.position.getComponent(2);
 | 
						||
};
 | 
						||
 | 
						||
CelestialBody.prototype.getRadius = function () {
 | 
						||
    if (this.objectGroup == null || this.objectGroup.position == null) return 0;
 | 
						||
    return this.radius;
 | 
						||
}; |