mirror of
https://github.com/ToxicCrack/PrintABrick.git
synced 2025-05-16 04:10:09 -07:00
Add 3D view toggle
This commit is contained in:
parent
db7195e4d3
commit
88c443b4ed
@ -1,45 +1,75 @@
|
||||
var ModelViewer = function($container) {
|
||||
var ModelViewer = function($dom_element) {
|
||||
var $this = this;
|
||||
this.container = $container;
|
||||
this.container = document.createElement('div');
|
||||
|
||||
this.dom_element = $dom_element;
|
||||
|
||||
$dom_element.append(this.container);
|
||||
this.container.style.display = "none";
|
||||
|
||||
this.camera = null;
|
||||
this.scene = null;
|
||||
this.renderer = null;
|
||||
this.controls = null;
|
||||
this.object = null;
|
||||
this.width = parseFloat(this.container.width());
|
||||
this.height = parseFloat(this.container.height());
|
||||
this.width = parseFloat($dom_element.width());
|
||||
this.height = parseFloat($dom_element.height());
|
||||
this.visible = true;
|
||||
this.stats = null;
|
||||
this.scale = 1;
|
||||
this.wireframe = false;
|
||||
this.rendering = false;
|
||||
|
||||
this.initHtml();
|
||||
this.initScene();
|
||||
|
||||
function renderLoop() {
|
||||
requestAnimationFrame( renderLoop );
|
||||
$this.render();
|
||||
requestAnimationFrame(renderLoop);
|
||||
if($this.rendering) {
|
||||
$this.render();
|
||||
}
|
||||
$this.stats.update();
|
||||
}
|
||||
|
||||
renderLoop();
|
||||
};
|
||||
|
||||
ModelViewer.prototype.initHtml = function () {
|
||||
$this = this;
|
||||
|
||||
var buttons = document.createElement("div");
|
||||
buttons.setAttribute("class", "modelviewer-buttons");
|
||||
|
||||
var toggleButton = $('<button/>', {
|
||||
'class':'toggle',
|
||||
'html':'<span>3D View</span>',
|
||||
'click': $this.toggleRendering.bind($this)
|
||||
}).appendTo(buttons);
|
||||
|
||||
this.dom_element.append(buttons);
|
||||
};
|
||||
|
||||
ModelViewer.prototype.initScene = function() {
|
||||
|
||||
this.scene = new THREE.Scene();
|
||||
this.scene.background = new THREE.Color( 0xffffff );
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 1000);
|
||||
this.camera.position.z = 8;
|
||||
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 300);
|
||||
this.camera.position.z = 7;
|
||||
this.camera.position.y = -5;
|
||||
this.camera.position.x = 3;
|
||||
this.camera.up = new THREE.Vector3(0, 0, 1);
|
||||
|
||||
this.reflectCamera = new THREE.CubeCamera(1, 50, 200);
|
||||
this.scene.add(this.reflectCamera);
|
||||
// this.reflectCamera = new THREE.CubeCamera(1, 50, 100);
|
||||
// this.scene.add(this.reflectCamera);
|
||||
|
||||
this.scene.fog = new THREE.FogExp2(0xbbbbbb, 0.2);
|
||||
|
||||
var material = new THREE.MeshPhongMaterial({
|
||||
color: 0xffffff,
|
||||
emissive: 0xffffff,
|
||||
shading: THREE.SmoothShading,
|
||||
fog: false,
|
||||
fog: true,
|
||||
side: THREE.BackSide
|
||||
});
|
||||
|
||||
@ -48,13 +78,13 @@ ModelViewer.prototype.initScene = function() {
|
||||
this.scene.add(skybox);
|
||||
|
||||
var groundPlaneMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0x888888,
|
||||
color: 0x999999,
|
||||
wireframe: false,
|
||||
transparent: true,
|
||||
opacity: 0.25,
|
||||
// fog: true,
|
||||
// specular: 0x999999,
|
||||
envMap: this.reflectCamera.renderTarget
|
||||
fog: false,
|
||||
specular: 0x999999,
|
||||
// envMap: this.reflectCamera.renderTarget
|
||||
});
|
||||
var x = 80;
|
||||
var y = 80;
|
||||
@ -86,47 +116,54 @@ ModelViewer.prototype.initScene = function() {
|
||||
this.renderer = new THREE.WebGLRenderer();
|
||||
|
||||
this.renderer.setSize(this.width, this.height);
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1);
|
||||
|
||||
|
||||
this.container.append(this.renderer.domElement);
|
||||
|
||||
// this.renderer.shadowMap.enabled = true;
|
||||
// this.renderer.shadowMap.renderReverseSided = false;
|
||||
|
||||
this.scene.fog = new THREE.FogExp2(0xcacaca, 0.9);
|
||||
|
||||
// // Light
|
||||
this.spotLight = new THREE.SpotLight(0xffffff, 0.9, 0);
|
||||
this.spotLight.position.set(-70, 100, 100);
|
||||
this.controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
|
||||
this.controls.enableZoom = true;
|
||||
|
||||
|
||||
this.initLights();
|
||||
|
||||
this.stats = new Stats();
|
||||
this.container.append( this.stats.dom );
|
||||
};
|
||||
|
||||
ModelViewer.prototype.initLights = function () {
|
||||
this.spotLight = new THREE.SpotLight(0xffffff, 0.8, 0);
|
||||
this.spotLight.position.set(-100, 100, 100);
|
||||
this.spotLight.castShadow = false;
|
||||
this.scene.add(this.spotLight);
|
||||
|
||||
this.bottomSpotLight = new THREE.SpotLight(0xffffff, 0.3, 0);
|
||||
this.bottomSpotLight = new THREE.SpotLight(0xffffff, 0.5, 0);
|
||||
this.bottomSpotLight.position.set(70, -100, -100);
|
||||
this.bottomSpotLight.castShadow = false;
|
||||
this.scene.add(this.bottomSpotLight);
|
||||
|
||||
this.ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
|
||||
this.scene.add(this.ambientLight);
|
||||
|
||||
this.pointLight = new THREE.PointLight(0xffaaff, 0.9, 0);
|
||||
this.pointLight = new THREE.PointLight(0xfdfdfd, 1, 0);
|
||||
this.pointLight.position.set(32, -39, 35);
|
||||
this.pointLight.castShadow = true;
|
||||
this.scene.add(this.pointLight);
|
||||
|
||||
this.renderer.shadowMap.enabled = true;
|
||||
this.renderer.shadowMap.renderReverseSided = false;
|
||||
|
||||
// $container.append(this.renderer.domElement);
|
||||
|
||||
this.controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
|
||||
this.controls.enableZoom = true;
|
||||
};
|
||||
|
||||
ModelViewer.prototype.addModel = function(geometry) {
|
||||
var material = new THREE.MeshPhongMaterial({
|
||||
color: 0x136FC3,
|
||||
color: 0x1379d7,
|
||||
specular: 0x0D0D0D,
|
||||
shading: THREE.SmoothShading,
|
||||
shininess: 150,
|
||||
shininess: 30,
|
||||
fog: false,
|
||||
side: THREE.DoubleSide,
|
||||
// wireframe: true,
|
||||
wireframe: this.wireframe,
|
||||
});
|
||||
|
||||
geometry.center();
|
||||
@ -179,7 +216,8 @@ ModelViewer.prototype.centerCamera = function(mesh) {
|
||||
|
||||
var geometry = mesh.geometry;
|
||||
|
||||
var distanceX = (geometry.boundingBox.max.x - geometry.boundingBox.min.x) / 2 / Math.tan(this.camera.fov * this.camera.aspect * Math.PI / 360);
|
||||
|
||||
var distanceX = ((geometry.boundingBox.max.x - geometry.boundingBox.min.x) / 2) / Math.tan(this.camera.fov * this.camera.aspect * Math.PI / 360);
|
||||
var distanceY = (geometry.boundingBox.max.y - geometry.boundingBox.min.y) / 2 / Math.tan(this.camera.fov * this.camera.aspect * Math.PI / 360);
|
||||
var distanceZ = (geometry.boundingBox.max.z - geometry.boundingBox.min.z) / 2 / Math.tan(this.camera.fov * Math.PI / 360);
|
||||
|
||||
@ -195,6 +233,16 @@ ModelViewer.prototype.centerCamera = function(mesh) {
|
||||
this.controls.reset();
|
||||
};
|
||||
|
||||
ModelViewer.prototype.toggleRendering = function () {
|
||||
if($this.rendering) {
|
||||
$this.container.style.display = "none";
|
||||
$this.rendering = false;
|
||||
} else {
|
||||
$this.container.style.display = "block";
|
||||
$this.rendering = true;
|
||||
}
|
||||
};
|
||||
|
||||
ModelViewer.prototype.objectCenter = function (mesh) {
|
||||
var middle = new THREE.Vector3();
|
||||
var geometry = mesh.geometry;
|
||||
|
25
app/Resources/assets/style/modelviewer.scss
Normal file
25
app/Resources/assets/style/modelviewer.scss
Normal file
@ -0,0 +1,25 @@
|
||||
.model-container {
|
||||
width:600px;
|
||||
height: 400px;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modelviewer-buttons {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#model-viewer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
@import "variables";
|
||||
|
||||
@import "main";
|
||||
@import "modelviewer";
|
||||
|
@ -7,44 +7,44 @@
|
||||
{% block header %}{{ model.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<dl>
|
||||
<dt>number:</dt><dd>{{ model.number }}</dd>
|
||||
<dt>name:</dt><dd>{{ model.name }}</dd>
|
||||
<dt>category:</dt><dd>{{ model.category ? model.category.name }}</dd>
|
||||
<dt>model:</dt><dd>{{ model.path }}</dd>
|
||||
<dt>author:</dt><dd>{{ model.author }}</dd>
|
||||
<dt>keywords:</dt>
|
||||
<dd>
|
||||
{% for keyword in model.keywords %}
|
||||
<span class="ui label">{{ keyword.name }}</span>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
<dt>aliases:</dt>
|
||||
<dd>
|
||||
{% for alias in model.aliases %}
|
||||
<span class="ui label">{{ alias.number }}</span>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
<dd>Download:</dd>
|
||||
<dt><a href="{{ url('media_file', {'path': model.path}) }}">{{ model.number }}</a></dt>
|
||||
<dt>rebrickable parts ({{ rbParts|length }}):</dt>
|
||||
<dd>
|
||||
<p>
|
||||
<div class="ui eight doubling cards">
|
||||
{% for alias in rbParts %}
|
||||
<a href="{{ url('rebrickable_part_show', {number:alias.number}) }}" class="ui label">{{ alias.number }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<div class="model-container">
|
||||
<img src="{{ asset('ldraw/images/'~model.number~'.png') | imagine_filter('model_large')}}">
|
||||
<div id="model-viewer"></div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap">
|
||||
<div id="model" style="height: 200px; width: 300px; padding: 5px; display: inline-block"></div>
|
||||
<div style="height: 300px; width: 300px; padding: 5px; display: inline-block">
|
||||
<img src="{{ url('media_file', {'path': 'ldraw/images/'~model.number~'.png'}) }}" style="max-height: 90%; max-width: 100%">
|
||||
</div>
|
||||
<dl>
|
||||
<dt>number:</dt><dd>{{ model.number }}</dd>
|
||||
<dt>name:</dt><dd>{{ model.name }}</dd>
|
||||
<dt>category:</dt><dd>{{ model.category ? model.category.name }}</dd>
|
||||
<dt>model:</dt><dd>{{ model.path }}</dd>
|
||||
<dt>author:</dt><dd>{{ model.author }}</dd>
|
||||
<dt>keywords:</dt>
|
||||
<dd>
|
||||
{% for keyword in model.keywords %}
|
||||
<span class="ui label">{{ keyword.name }}</span>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
<dt>aliases:</dt>
|
||||
<dd>
|
||||
{% for alias in model.aliases %}
|
||||
<span class="ui label">{{ alias.number }}</span>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
<dd>Download:</dd>
|
||||
<dt><a href="{{ url('media_file', {'path': model.path}) }}">{{ model.number }}</a></dt>
|
||||
<dt>rebrickable parts ({{ rbParts|length }}):</dt>
|
||||
<dd>
|
||||
<p>
|
||||
<div class="ui eight doubling cards">
|
||||
{% for alias in rbParts %}
|
||||
<a href="{{ url('rebrickable_part_show', {number:alias.number}) }}" class="ui label">{{ alias.number }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div>
|
||||
<h4 class="ui horizontal divider header">
|
||||
<i class="puzzle icon"></i> Subparts of this model
|
||||
</h4>
|
||||
@ -76,7 +76,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
modelView = new ModelViewer($('#model'));
|
||||
modelView = new ModelViewer($('#model-viewer'));
|
||||
modelView.loadStl('{{ url('media_file', {'path': model.path }) }}');
|
||||
};
|
||||
</script>
|
||||
|
@ -112,6 +112,14 @@ liip_imagine:
|
||||
quality: 80
|
||||
filters:
|
||||
thumbnail: { size: [200, 200], mode: inset }
|
||||
background: { size: [250, 250], position: center, color: '#FFFFFF' }
|
||||
model_large:
|
||||
data_loader: media
|
||||
cache: ~
|
||||
quality: 92
|
||||
filters:
|
||||
thumbnail: { size: [400, 300], mode: inset }
|
||||
background: { size: [600, 400], position: center, color: '#FFFFFF' }
|
||||
rebrickable_part_min:
|
||||
quality: 80
|
||||
data_loader: rebrickable
|
||||
@ -125,11 +133,11 @@ liip_imagine:
|
||||
filters:
|
||||
thumbnail: { size: [600, 600], mode: inset }
|
||||
rebrickable_set_min:
|
||||
quality: 80
|
||||
data_loader: rebrickable
|
||||
cache: ~
|
||||
filters:
|
||||
thumbnail: { size: [300, 300], mode: inset }
|
||||
quality: 80
|
||||
data_loader: rebrickable
|
||||
cache: ~
|
||||
filters:
|
||||
thumbnail: { size: [300, 300], mode: inset }
|
||||
|
||||
oneup_flysystem:
|
||||
adapters:
|
||||
|
@ -32,6 +32,7 @@ gulp.task('js', function() {
|
||||
'node_modules/jquery/dist/jquery.js',
|
||||
'node_modules/semantic-ui/dist/semantic.js',
|
||||
'app/Resources/assets/js/**.js',
|
||||
'node_modules/three/examples/js/libs/stats.min.js'
|
||||
])
|
||||
.pipe(plugins.concat('main.js'))
|
||||
.pipe(gulp.dest('web/resources/js'));
|
||||
@ -45,7 +46,7 @@ gulp.task('files', function () {
|
||||
|
||||
gulp.task('watch', ['js', 'css', 'three'], function () {
|
||||
gulp.watch('app/Resources/assets/js/**.js' , ['js']);
|
||||
gulp.watch('app/Resources/assets/style/**/*.sass' , ['css']);
|
||||
gulp.watch('app/Resources/assets/style/**/*.scss' , ['css']);
|
||||
});
|
||||
|
||||
gulp.task('default', function () {
|
||||
|
Loading…
x
Reference in New Issue
Block a user