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 @@
@@ -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();
+ }
}