1
0
web/static/pkg/solar-sys/js/CelestialBody.js
2024-04-28 20:12:46 +09:00

407 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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