Skip to content

Commit

Permalink
Merge branch 'main' into 188071551-user-locations-setup
Browse files Browse the repository at this point in the history
  • Loading branch information
bacalj committed Aug 8, 2024
2 parents 5cdc908 + cc11473 commit 0c290c1
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 34 deletions.
26 changes: 18 additions & 8 deletions src/grasp-seasons/3d-models/common-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import * as data from "../utils/solar-system-data";
import * as c from "./constants";
import { IModelParams } from "../types";

import SunPNG from "../assets/[email protected]";

function addEdges(mesh: THREE.Mesh) {
const geometry = new THREE.EdgesGeometry(mesh.geometry);
const material = new THREE.LineBasicMaterial({ color: 0x000000 });
Expand Down Expand Up @@ -56,14 +58,23 @@ export default {
return light;
},

sun (params: IModelParams) {
sun2 (params: IModelParams) {
const radius = params.type === "orbit-view" ? c.SIMPLE_SUN_RADIUS : c.SUN_RADIUS;
const geometry = new THREE.SphereGeometry(radius, 32, 32);
const material = new THREE.MeshPhongMaterial({ emissive: c.SUN_COLOR, color: 0x000000 });
const mesh = new THREE.Mesh(geometry, material);
return mesh;
},

sun (params: IModelParams) {
const texture = new THREE.TextureLoader().load(SunPNG);
const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false });
const sprite = new THREE.Sprite(material);
sprite.renderOrder = 1;
sprite.scale.set(100000000 * c.SF, 100000000 * c.SF, 1);
return sprite;
},

earth (params: IModelParams) {
const simple = params.type === "orbit-view";
const RADIUS = simple ? c.SIMPLE_EARTH_RADIUS : c.EARTH_RADIUS;
Expand Down Expand Up @@ -96,13 +107,13 @@ export default {
// Load font in a sync way, using webpack raw-loader. Based on async THREE JS loader:
// https://github.com/mrdoob/three.js/blob/ddab1fda4fd1e21babf65aa454fc0fe15bfabc33/src/loaders/FontLoader.js#L20
const font = new Font(museo500FontDef as any);
const SIZE = 16000000;
const SIZE = 13000000;
const HEIGHT = 1000000;
const SIZE_SMALL = SIZE / 2;
const HEIGHT_SMALL = HEIGHT / 2;
const SIZE_SMALL = SIZE;
const HEIGHT_SMALL = HEIGHT;

const COLOR = 0xffff00;
const COLOR_SMALL = 0x999966;
const COLOR_SMALL = 0xffffff;

const geometry = new TextGeometry(txt, {
size: small ? SIZE_SMALL * c.SF : SIZE * c.SF,
Expand All @@ -122,12 +133,11 @@ export default {
},

grid (params: IModelParams) {
const simple = params.type === "orbit-view";
const RAY_COUNT = 24;
const RAY_COUNT = 12;
const DAY_COUNT = 365;
const STEP = DAY_COUNT / RAY_COUNT;
const geometry = new THREE.BufferGeometry();
const material = new THREE.LineBasicMaterial({ color: 0xffff00, transparent: true, opacity: simple ? 0.4 : 0.6 });
const material = new THREE.LineBasicMaterial({ color: 0xffff00, transparent: true, opacity: 0.7 });
const vertices = new Float32Array(2 * RAY_COUNT * 3);
for (let i = 0; i < RAY_COUNT; ++i) {
vertices[i * 6] = 0;
Expand Down
6 changes: 3 additions & 3 deletions src/grasp-seasons/3d-models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import * as data from "../utils/solar-system-data";
export const SF = 1 / data.SCALE_FACTOR;

export const EARTH_RADIUS = 7000000 * SF;
export const SIMPLE_EARTH_RADIUS = 10000000 * SF;
export const SIMPLE_EARTH_RADIUS = 13000000 * SF;
export const SUN_RADIUS = 4000000 * SF;
export const SIMPLE_SUN_RADIUS = 15000000 * SF;
export const SIMPLE_SUN_RADIUS = 25000000 * SF;
export const LATLNG_MARKER_RADIUS = 300000 * SF;

export const LAT_LINE_THICKNESS = 0.01;

export const SUN_COLOR = 0xCB671F;
export const SUN_COLOR = 0xdcdca3;
export const HIGHLIGHT_COLOR = 0xff0000;
export const HIGHLIGHT_EMISSIVE = 0xbb3333;
10 changes: 4 additions & 6 deletions src/grasp-seasons/3d-models/earth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as data from "../utils/solar-system-data";
import * as c from "./constants";
import earthLargeImg from "../assets/earth-2k.jpg";//'../assets/earth-grid-2k.jpg';
import earthLargeGridImg from "../assets/earth-grid-2k.jpg";
import earthSimpleImg from "../assets/earth-equator-0.5k.jpg";//'../assets/earth-0.5k.jpg';
import earthBumpImg from "../assets/earth-bump-2k.jpg";
import { IModelParams } from "../types";
// import earthSpecularImg from '../assets/earth-specular-2k.png';
Expand All @@ -17,18 +16,17 @@ export default class Earth {
_orbitRotationObject: THREE.Object3D;
_posObject: THREE.Object3D;
_tiltObject: THREE.Object3D;

constructor(params: IModelParams) {
const simple = params.type === "orbit-view";
const RADIUS = simple ? c.SIMPLE_EARTH_RADIUS : c.EARTH_RADIUS;
const COLORS = simple ? { color: DEF_COLOR, emissive: DEF_EMISSIVE, specular: 0x000000 } : { specular: 0x000000 };
const geometry = new THREE.SphereGeometry(RADIUS, 64, 64);
this._material = new THREE.MeshPhongMaterial(COLORS);
const textureLoader = new THREE.TextureLoader();
this._material.map = textureLoader.load(simple ? earthSimpleImg : earthLargeImg);
if (!simple) {
this._material.bumpMap = textureLoader.load(earthBumpImg);
this._material.bumpScale = 100000 * c.SF;
}
this._material.map = textureLoader.load(earthLargeImg);
this._material.bumpMap = textureLoader.load(earthBumpImg);
this._material.bumpScale = 100000 * c.SF;
this._earthObject = new THREE.Mesh(geometry, this._material);
this._orbitRotationObject = new THREE.Object3D();
this._orbitRotationObject.add(this._earthObject);
Expand Down
4 changes: 2 additions & 2 deletions src/grasp-seasons/3d-models/sun-earth-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ export default class {
}

_initArrow(simple: boolean) {
const HEIGHT = simple ? 25000000 * c.SF : 2500000 * c.SF;
const HEIGHT = simple ? 115000000 * c.SF : 2500000 * c.SF;
const RADIUS = simple ? 1500000 * c.SF : 100000 * c.SF;
const HEAD_RADIUS = RADIUS * (simple ? 2.5 : 2);
const HEAD_RADIUS = RADIUS * (simple ? 7 : 2);
const HEAD_HEIGHT = HEIGHT * 0.2;
const geometry = new THREE.CylinderGeometry(RADIUS, RADIUS, HEIGHT, 32);
const material = new THREE.MeshPhongMaterial({ color: 0xff0000, emissive: c.SUN_COLOR });
Expand Down
3 changes: 2 additions & 1 deletion src/grasp-seasons/3d-views/base-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default class BaseView {
scene: THREE.Scene;
sunEarthLine!: SunEarthLine;
type: ModelType;

constructor(parentEl: HTMLElement, props: Partial<ISimState> = DEF_PROPERTIES, modelType: ModelType = "unknown") {
const width = parentEl.clientWidth;
const height = parentEl.clientHeight;
Expand Down Expand Up @@ -137,7 +138,7 @@ export default class BaseView {
}
}

registerInteractionHandler(handler: BaseInteraction) {
registerInteractionHandler(handler: BaseInteraction | null) {
this._interactionHandler = handler;
}

Expand Down
106 changes: 100 additions & 6 deletions src/grasp-seasons/3d-views/orbit-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,39 @@ const DEF_PROPERTIES = {
sunEarthLine: true
};

const CAMERA_TWEEN_LENGTH = 1000;

// Cubic Bezier function
function cubicBezier(t: number, p0: number, p1: number, p2: number, p3: number): number {
const oneMinusT = 1 - t;
return Math.pow(oneMinusT, 3) * p0 +
3 * Math.pow(oneMinusT, 2) * t * p1 +
3 * oneMinusT * Math.pow(t, 2) * p2 +
Math.pow(t, 3) * p3;
}

// Ease-in-out function
function easeInOut(t: number): number {
const p0 = 0, p1 = 0.25, p2 = 0.75, p3 = 1;
return cubicBezier(t, p0, p1, p2, p3);
}

export default class OrbitView extends BaseView {
cameraSymbol!: THREE.Object3D;
latLine!: LatitudeLine;
latLongMarker!: LatLongMarker;
monthLabels!: THREE.Object3D[];
earthDraggingInteraction: EarthDraggingInteraction = new EarthDraggingInteraction(this);

startingCameraPos?: THREE.Vector3;
desiredCameraPos?: THREE.Vector3;
startingCameraLookAt?: THREE.Vector3;
desiredCameraLookAt?: THREE.Vector3;
cameraSwitchTimestamp?: number;

constructor(parentEl: HTMLElement, props = DEF_PROPERTIES) {
super(parentEl, props, "orbit-view");
this.registerInteractionHandler(new EarthDraggingInteraction(this));
this.registerInteractionHandler(this.earthDraggingInteraction);
}

setViewAxis(vec3: THREE.Vector3) {
Expand Down Expand Up @@ -55,10 +80,50 @@ export default class OrbitView extends BaseView {
};
}

getCloseUpCameraPosition() {
const cameraOffset = new THREE.Vector3(0, 0, 40000000 / data.SCALE_FACTOR);
return this.earth.posObject.position.clone().add(cameraOffset);
}

setupEarthCloseUpView() {
this.registerInteractionHandler(null); // disable dragging interaction in close-up view
this.sunEarthLine.rootObject.visible = false;
this.monthLabels.forEach((label) => label.visible = false);
}

setupOrbitView() {
this.registerInteractionHandler(this.earthDraggingInteraction);
this.sunEarthLine.rootObject.visible = true;
this.monthLabels.forEach((label) => label.visible = true);
}

render(timestamp: number) {
super.render(timestamp);

if (this.desiredCameraPos && this.startingCameraPos && this.desiredCameraLookAt && this.startingCameraLookAt &&
this.cameraSwitchTimestamp !== undefined
) {
const progress = Math.max(0, Math.min(1, (Date.now() - this.cameraSwitchTimestamp) / CAMERA_TWEEN_LENGTH));
const progressEased = easeInOut(progress);

this.camera.position.lerpVectors(this.startingCameraPos, this.desiredCameraPos, progressEased);
this.controls.target.lerpVectors(this.startingCameraLookAt, this.desiredCameraLookAt, progressEased);
if (progress === 1) {
this.startingCameraPos = undefined;
this.desiredCameraPos = undefined;
this.startingCameraLookAt = undefined;
this.desiredCameraLookAt = undefined;
this.cameraSwitchTimestamp = undefined;
}
}
}

getInitialCameraPosition() {
return new THREE.Vector3(0, 440000000 / data.SCALE_FACTOR, 0);
}

_setInitialCamPos() {
this.camera.position.x = 0;
this.camera.position.y = 360000000 / data.SCALE_FACTOR;
this.camera.position.z = 0;
this.camera.position.copy(this.getInitialCameraPosition());
}

toggleCameraModel(show: boolean) {
Expand Down Expand Up @@ -91,6 +156,35 @@ export default class OrbitView extends BaseView {
this.latLongMarker.setLatLong(this.props.lat, this.props.long);
}

_updateEarthCloseUpView() {
this.startingCameraPos = this.camera.position.clone();
this.startingCameraLookAt = this.controls.target.clone();
this.cameraSwitchTimestamp = Date.now();

if (this.props.earthCloseUpView) {
this.desiredCameraPos = this.getCloseUpCameraPosition();
this.desiredCameraLookAt = this.earth.posObject.position.clone();
this.setupEarthCloseUpView();
} else {
this.desiredCameraPos = this.getInitialCameraPosition();
this.desiredCameraLookAt = new THREE.Vector3(0, 0, 0);
this.setupOrbitView();
}
}

_updateDay(): void {
super._updateDay();
if (this.props.earthCloseUpView) {
if (this.desiredCameraPos && this.desiredCameraLookAt) {
this.desiredCameraPos.copy(this.getCloseUpCameraPosition());
this.desiredCameraLookAt.copy(this.earth.posObject.position);
} else {
this.camera.position.copy(this.getCloseUpCameraPosition());
this.controls.target.copy(this.earth.posObject.position);
}
}
}

_initScene() {
super._initScene();
this.latLine = new LatitudeLine(false, true);
Expand All @@ -106,12 +200,12 @@ export default class OrbitView extends BaseView {
const months = this.months;
const segments = months.length;
const arc = 2 * Math.PI / segments;
const labelRadius = data.EARTH_ORBITAL_RADIUS * 1.15;
const labelRadius = data.EARTH_ORBITAL_RADIUS * 1.22;

const monthLabels: THREE.Object3D[] = [];

for (let i = 0; i < months.length; i++) {
const monthLbl = models.label(months[i], months[i].length === 3);
const monthLbl = models.label(months[i], i % 3 !== 0);
const angle = i * arc;
monthLbl.position.x = labelRadius * Math.sin(angle);
monthLbl.position.z = labelRadius * Math.cos(angle);
Expand Down
Binary file added src/grasp-seasons/assets/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion src/grasp-seasons/components/seasons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,32 @@ body {
width: 378px;
height: 374px;
position: relative;
color: #fff;

.playback-controls {
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
flex-direction: row;
color: #fff;

label {
font-weight: normal;
}
}

.view-type-dropdown {
position: absolute;
top: 10px;
display: flex;
justify-content: center;
width: 100%;

select {
margin-left: 7px;
width: 150px;
}
}
}

.ground-view-label {
Expand Down
23 changes: 21 additions & 2 deletions src/grasp-seasons/components/seasons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ const DEFAULT_SIM_STATE: ISimState = {
sunrayDistMarker: false,
dailyRotation: false,
earthGridlines: false,
lang: "en_us",
// -- Day Length Plugin extra state ---
// It could be ported back to GRASP Seasons too to handle camera model cleaner way.
showCamera: false,
lang: "en_us"
// A new type of view where the camera is locked on Earth. It is different from GRASP Seasons Earth View because the
// camera follows Earth's position but does not rotate. As the year passes, we'll see different parts of Earth,
// including its night side. This is useful for keeping the Earth's axis constant.
earthCloseUpView: false,
};

function capitalize(string: string) {
Expand Down Expand Up @@ -83,7 +89,7 @@ const Seasons: React.FC<IProps> = ({ lang = "en_us", initialState = {}, log = (a

// Derived state
const simLang = simState.lang;
const playStopLabel = mainAnimationStarted ? t("~STOP", simLang) : t("~PLAY", simLang);
const playStopLabel = mainAnimationStarted ? t("~STOP", simLang) : t("~ORBIT_BUTTON", simLang);

// Log helpers
const logCheckboxChange = (event: ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -160,6 +166,11 @@ const Seasons: React.FC<IProps> = ({ lang = "en_us", initialState = {}, log = (a
});
};

const handleViewChange = (event: ChangeEvent<HTMLSelectElement>) => {
const earthCloseUpView = event.target.value === "true";
setSimState(prevState => ({ ...prevState, earthCloseUpView }));
}

const solarIntensityValue = getSolarNoonIntensity(simState.day, simState.lat).toFixed(2);

return (
Expand All @@ -169,6 +180,14 @@ const Seasons: React.FC<IProps> = ({ lang = "en_us", initialState = {}, log = (a
<OrbitViewComp
ref={orbitViewRef} simulation={simState} onSimStateChange={handleSimStateChange} log={log} showCamera={false}
/>
<div className="view-type-dropdown">
<label>{ t("~VIEW", simLang) }
<select value={simState.earthCloseUpView.toString()} onChange={handleViewChange}>
<option value="false">{ t("~EARTH_ORBIT", simLang) }</option>
<option value="true">{ t("~EARTH_CLOSE_UP", simLang) }</option>
</select>
</label>
</div>
<div className="playback-controls">
<button
className="btn btn-default animation-btn"
Expand Down
Loading

0 comments on commit 0c290c1

Please sign in to comment.