// 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|)) * 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; };