diff --git a/README.md b/README.md index f5f917d..c7dc389 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ Three.js powered Minecraft skin viewer. // Set the background to a panoramic image! skinViewer.loadPanorama("img/panorama1.png"); + // Change camera FOV + skinViewer.fov = 70; + // Control objects with your mouse! let control = skinview3d.createOrbitControls(skinViewer); control.enableRotate = true; diff --git a/examples/index.html b/examples/index.html index 3fdaffe..191ab4d 100644 --- a/examples/index.html +++ b/examples/index.html @@ -111,9 +111,14 @@
-

Canvas Size

- - +

Viewport

+
+ + +
+
+ +
@@ -358,6 +363,7 @@ function initializeControls() { document.getElementById("canvas_width").addEventListener("change", e => skinViewer.width = e.target.value); document.getElementById("canvas_height").addEventListener("change", e => skinViewer.height = e.target.value); + document.getElementById("fov").addEventListener("change", e => skinViewer.fov = e.target.value); document.getElementById("global_animation_speed").addEventListener("change", e => skinViewer.animations.speed = e.target.value); document.getElementById("animation_pause_resume").addEventListener("click", () => skinViewer.animations.paused = !skinViewer.animations.paused); document.getElementById("rotate_animation").addEventListener("change", e => { @@ -456,6 +462,7 @@ skinViewer.width = document.getElementById("canvas_width").value; skinViewer.height = document.getElementById("canvas_height").value; + skinViewer.fov = document.getElementById("fov").value; skinViewer.animations.speed = document.getElementById("global_animation_speed").value; if (document.getElementById("rotate_animation").checked) { rotateAnimation = skinViewer.animations.add(skinview3d.RotatingAnimation); diff --git a/src/orbit_controls.ts b/src/orbit_controls.ts index 8020c60..f6a02e1 100644 --- a/src/orbit_controls.ts +++ b/src/orbit_controls.ts @@ -7,7 +7,7 @@ export function createOrbitControls(skinViewer: SkinViewer): OrbitControls { // default configuration control.enablePan = false; - control.target = new Vector3(0, -8, 0); + control.target = new Vector3(0, 0, 0); control.minDistance = 10; control.maxDistance = 256; control.update(); diff --git a/src/viewer.ts b/src/viewer.ts index 0bf5cb0..06ab812 100644 --- a/src/viewer.ts +++ b/src/viewer.ts @@ -1,5 +1,5 @@ import { inferModelType, isTextureSource, loadCapeToCanvas, loadImage, loadSkinToCanvas, ModelType, RemoteImage, TextureSource } from "skinview-utils"; -import { Color, ColorRepresentation, EquirectangularReflectionMapping, NearestFilter, PerspectiveCamera, Scene, Texture, Vector2, WebGLRenderer } from "three"; +import { Color, ColorRepresentation, EquirectangularReflectionMapping, Group, NearestFilter, PerspectiveCamera, Scene, Texture, Vector2, WebGLRenderer } from "three"; import { RootAnimation } from "./animation.js"; import { BackEquipment, PlayerObject } from "./model.js"; @@ -57,6 +57,12 @@ export interface SkinViewerOptions { * The panorama background to use. This option overrides 'background' option. */ panorama?: RemoteImage | TextureSource; + + /** + * Camera vertical field of view, in degrees. Default is 50. + * The distance between the object and the camera is automatically computed. + */ + fov?: number; } export class SkinViewer { @@ -65,6 +71,7 @@ export class SkinViewer { readonly camera: PerspectiveCamera; readonly renderer: WebGLRenderer; readonly playerObject: PlayerObject; + readonly playerWrapper: Group; readonly animations: RootAnimation = new RootAnimation(); readonly skinCanvas: HTMLCanvasElement; @@ -96,9 +103,7 @@ export class SkinViewer { this.scene = new Scene(); - // Use smaller fov to avoid distortion - this.camera = new PerspectiveCamera(40); - this.camera.position.y = -8; + this.camera = new PerspectiveCamera(); this.camera.position.z = 60; this.renderer = new WebGLRenderer({ @@ -113,7 +118,10 @@ export class SkinViewer { this.playerObject.name = "player"; this.playerObject.skin.visible = false; this.playerObject.cape.visible = false; - this.scene.add(this.playerObject); + this.playerWrapper = new Group(); + this.playerWrapper.add(this.playerObject); + this.playerWrapper.position.y = 8; + this.scene.add(this.playerWrapper); if (options.skin !== undefined) { this.loadSkin(options.skin, options.model); @@ -133,6 +141,7 @@ export class SkinViewer { if (options.panorama !== undefined) { this.loadPanorama(options.panorama); } + this.fov = options.fov === undefined ? 50 : options.fov; if (options.renderPaused === true) { this._renderPaused = true; @@ -333,4 +342,18 @@ export class SkinViewer { this.backgroundTexture = null; } } + + get fov(): number { + return this.camera.fov; + } + + set fov(value: number) { + this.camera.fov = value; + let distance = 4 + 20 / Math.tan(value / 180 * Math.PI / 2); + if (distance < 10) { + distance = 10; + } + this.camera.position.multiplyScalar(distance / this.camera.position.length()); + this.camera.updateProjectionMatrix(); + } }