Hi I want to use a GLTF with draco. I get the object but when adding it to the scene it says:
components:gltf-model:warn THREE.GLTFLoader: No DRACOLoader instance provided.
I have my dracoDecoderPath set inside my a-scene:
<a-scene
sceneviewer
gltf-model="dracoDecoderPath: https://www.gstatic.com/draco/v1/decoders/;"
vr-mode-ui="enabled: false"
embedded
arjs='sourceType: webcam; sourceWidth:1280; sourceHeight:960; displayWidth: 1280; displayHeight: 960; debugUIEnabled: false;'>
AFRAME.registerComponent('sceneviewer', {
init: function () {
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderConfig({ type: 'js' });
dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.4.0/");
this.el.sceneEl.systems["gltf-model"].data.dracoDecoderPath = 'https://www.gstatic.com/draco/versioned/decoders/1.4.0/';
this.el.sceneEl.systems["gltf-model"].dracoLoader = dracoLoader;
},
Related
I'm facing an error when implementing three JS Gltf Loader with nuxt 3.
Error message :
" Uncaught (in promise) TypeError: Class constructor Loader cannot be invoked without 'new' .. "
versions:
"three": "^0.148.0",
"three-gltf-loader": "^1.111.0"
<template>
<div ref="container"></div>
</template>
<script>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import GLTFLoader from "three-gltf-loader";
export default {
setup() {
const container = ref(null);
const scene = ref(new THREE.Scene());
const renderer = ref(new THREE.WebGLRenderer({ antialias: true }));
const width = 700;
const height = 700;
const camera = ref(
new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
);
const loader = ref(new GLTFLoader());
onMounted(async () => {
renderer.value.setSize(
container.value.clientWidth,
container.value.clientHeight
);
container.value.appendChild(renderer.value.domElement);
camera.value.position.z = 5;
const response = await fetch("logo.gltf");
const gltf = await response.json();
loader.value.parse(
gltf,
"",
(gltf) => {
scene.value.add(gltf.scene);
renderer.value.render(scene.value, camera.value);
},
undefined,
(error) => {
console.error(error);
}
);
});
return { container };
},
};
</script>
"three": "^0.148.0", "three-gltf-loader": "^1.111.0"
This kind of setup isn't recommended since you can import the latest GLTFLoader module from the three repository. Try it again with these imports:
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
I found a solution on Alvaro Dev Labs' YT channel, he used TroisJS instead of ThreeJS ( in fact, it's Vite + Three).
And for nuxt 3 you just need to add ".client.vue " to the file name of your component to run it on the client side (Model.client.vue).
<script lang="ts">
import { defineComponent, ref, onMounted } from "vue";
import {
Renderer,
Scene,
Camera,
PointLight,
AmbientLight,
GltfModel,
} from "troisjs";
export default defineComponent({
components: {
Renderer,
Scene,
Camera,
PointLight,
AmbientLight,
GltfModel,
},
setup() {
const renderer = ref(null);
const model = ref(null);
function onReady(model) {
console.log("Ready", model);
}
onMounted(() => {
renderer?.value?.onBeforeRender(() => {
model.value.rotation.x += 0.01;
});
});
return {
renderer,
model,
onReady,
};
},
});
</script>
<template>
<div>
<Renderer ref="renderer" antialias orbit-ctrl resize="window">
<Camera :position="{ x: -10, z: 20 }" />
<Scene background="#fff">
<AmbientLight />
<PointLight
color="white"
:position="{ x: 100, y: 1000, z: 40 }"
:intensity="1"
/>
<GltfModel ref="model" src="/Models/logo.gltf" #load="onReady" />
</Scene>
</Renderer>
</div>
</template>
import like this
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
Then use like this
const loader = new GLTFLoader()
I am creating a plugin for CKEditor 5, and I can't figure out how to set the title attribute on an <img> element using writer.setAttribute('title', ...).
I have tried stuff like schema.extend but to no avail.
The thing is, code works flawlessly when operating on the alt attribute.
Am I missing something?
My plugin code:
const ButtonView = require('#ckeditor/ckeditor5-ui/src/button/buttonview').default;
const imageIcon = require('#ckeditor/ckeditor5-core/theme/icons/low-vision.svg').default;
export default class ImageTextTitle extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add('imageTextTitle', locale => {
const view = new ButtonView(locale);
view.set({
label: 'Insert image title',
icon: imageIcon,
tooltip: true
});
view.on('execute', () => {
const newTitle = prompt('New image title');
const selection = editor.model.document.selection;
const imageElement = selection.getSelectedElement();
if (newTitle !== null) {
editor.model.change(writer => {
writer.setAttribute('title', newTitle, imageElement);
});
}
});
return view;
});
}
}
In Openlayers 6 each layer has an independent renderer (previously, all layer rendering was managed by a single map renderer and depended on a single rendering strategy - https://openlayers.org/workshop/en/webgl/meteorites.html). In my project I have more then 20 TileLayers (TileWMS), and the loading, panning, scrolling performance worse then in openlayers 5. Can I set the rendering strategy? How can I increase performance?
The tiles are loading fast, but then (after loading tiles) panning on map is slow. The GPU usage not critical (below 30%)
Angular 9 project, logic in service classes:
#Injectable({
providedIn: 'root'
})
export class EMap {
private eMap: OlMap;
public createMapObject(): void {
this.eMap = new OlMap({
layers: [],
view: new View({
projection,
resolutions: resolutionsArray,
constrainResolution: true,
enableRotation: false
}),
controls: defaultControls({
rotate: false,
attribution: false,
zoom: false
}).extend([
mousePositionControl,
scalelineControl
])
});
}
public initMap(center: Coordinate, zoom: number, target: string): void {
this.eMap.getView().setCenter(center);
this.eMap.getView().setZoom(zoom);
this.eMap.setTarget(target);
}
public addLayer(layer: TileLayer | ImageLayer | VectorLayer): void {
this.eMap.addLayer(layer);
}
}
#Injectable({
providedIn: 'root'
})
export class EMapSupportlayers extends EMapNetworklayers {
constructor(private readonly eMap: EMap) {}
public addTilelayer(networklayerInfo: NetworklayerInfo): void {
const layer: TileLayer = this.createTileLayer(tileLayerInitValues);
this.eMap.addLayer(layer);
}
private createTileLayer(tileLayerInitValues: TileLayerInitValues): TileLayer {
const tileGrid: TileGrid = new TileGrid({
extent: tileLayerInitValues.tileGridExtent,
resolutions: tileLayerInitValues.resolutions,
tileSize: tileLayerInitValues.tileSize
});
const source = new TileWMS({
url: tileLayerInitValues.url,
params: {
LAYERS: tileLayerInitValues.layerName,
FORMAT: tileLayerInitValues.layerFormat
},
tileLoadFunction: (image: any, src: string) => this.customLoader(image, src),
tileGrid
});
return new TileLayer({
visible: tileLayerInitValues.visible,
maxZoom: tileLayerInitValues.maxZoom,
minZoom: ttileLayerInitValues.minZoom,
source,
zIndex: tileLayerInitValues.zindex
});
}
private async customLoader(tile: any, sourceUrl: string): Promise<void> {
const response = await fetch(sourceUrl, {
method: 'POST',
credentials: 'include',
headers: new Headers({
Authorization: `Bearer ${...}`
}),
body: requestBody ? requestBody : null
});
const blob = await response.blob();
tile.getImage().src = URL.createObjectURL(blob);
}
}
--- 07.19.
I have created a dummy axample (Angular9, Openlayers 6.3.1):
Layers tiles are loading fast. On small screen panning is fast, but on large screen panning is slow (after loading and cacheing tiles). The performance was better in openlayers 5.
import { AfterViewInit, Component } from '#angular/core';
import TileLayer from 'ol/layer/Tile';
import Map from 'ol/Map';
import { OSM } from 'ol/source';
import View from 'ol/View';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
ngAfterViewInit(): void {
const mapElement = document.createElement('div');
mapElement.style.cssText = 'position:absolute;width:100%;height:100%';
const layers = [];
for (let i = 0; i < 30; ++i) {
const layer = new TileLayer({
source: new OSM(),
// className: 'layer' + i => create own canvas by layers, same performance
});
layer.setOpacity(0.03);
layers.push(layer);
}
const map = new Map({
layers,
view: new View({
center: [0, 0],
zoom: 1
})
});
document.body.appendChild(mapElement);
map.setTarget(mapElement);
}
}
I have found a solution, not perfect, but the performance is better.
map.on('movestart', () => {
layers.forEach(layer => {
layer.setExtent(map.getView().calculateExtent());
});
});
map.on('moveend', () => {
layers.forEach(layer => {
layer.setExtent(undefined);
});
});
URL.createObjectURL can cause memory leaks, try
const blob = await response.blob();
const objectURL = URL.createObjectURL(blob)
tile.getImage().onload = function(){
URL.revokeObjectURL(objectURL);
};
tile.getImage().src = objectURL;
Also do any of your layers use the same WMS URL with a diiferent WMS layerName? It would be more efficient to combine them into a single OpenLayers layer with a list of WMS layer names in the LAYERS parameter.
I made a inline widget similar a placeholder (ckeditor4), but now I want to render a dropdown when the widget is selected to show options values to replace the placeholder. I trying use BalloonPanelView but no success until now, someone have a idea about how to make it?
this.editor.editing.view.document.on('click', (evt, data) => {
evt.stop();
const element = data.target;
if (element && element.hasClass('placeholder')) {
if (!element.getAttribute('data-is-fixed')) {
const balloonPanelView = new BalloonPanelView();
balloonPanelView.render();
['option1', 'option2', 'option3'].forEach((value) => {
const view = new View();
view.set({
label: value,
withText: true
});
balloonPanelView.content.add(view);
});
balloonPanelView.pin({
target: element
});
}
}
});
I found the solution using ContextualBalloon class:
import ContextualBalloon from "#ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon";
// Define ballon
const balloon = editor.plugins.get(ContextualBalloon);
const placeholderOptions = // Here I defined list with buttons '<li><button></li>'
// Finnaly render ballon
balloon.add({
view: placeholderOptions,
singleViewMode: true,
position: {
target: data.domTarget
}
});
var ContactManager = new Marionette.Application();
ContactManager.addRegions({
mainRegion: "#main-region",
child:"#child2"
});
Ar = Backbone.Model.extend({});
Se = Backbone.Model.extend({});
Articlescollection = new Ar({ product_id: "104", title: "Test title"});
SelectedsCollection = new Se({ product_id: "71", title: "Test title"});
ContactManager.StaticView = Marionette.ItemView.extend({
template: tpl2,
tagName: "div",
model:Articlescollection,
modelEvents: {
'change': 'fieldsChanged'
},
fieldsChanged:function(){
console.log('dddd')
},
initialize: function () {
this.model.on('change', this.render);
}
});
ContactManager.StaticView2 = Marionette.ItemView.extend({
template: tpl2,
tagName: "div",
model:SelectedsCollection
});
var MyLayout = Backbone.Marionette.LayoutView.extend({
template: tpl3,
regions: {
menu: "#menu",
content: "#content"
}
});
ContactManager.on("start", function() {
// ContactManager.mainRegion.show( new MyLayout )
var layout = new MyLayout
ContactManager.mainRegion.show( layout )
layout.menu.show(new ContactManager.StaticView());
layout.content.show(new ContactManager.StaticView2())
Articlescollection.set("product_id", 24)
//init fieldsChanged trigger for change model
})
ContactManager.start();
What differences between modelEvents and this.model.on ?
they both initizlized when model was change but
modelEvents: {
'change': this.render
},
throw exception Uncaught TypeError: Cannot read property 'split' of undefined
modelEvents is the same as this.listenTo(this.model, { 'change': 'fieldsChanged' }); It is just sugar so you don't have to add that to initialize. You should probably never use this.model.on inside a view. That would not get cleaned up automatically like this.listenTo would. Other than this.on I don't think on should be used in general as listenTo is much safer.
The other major difference here is that:
var model = this.model;
var view = this;
this.model.on('change', function() {
this === model; // true
this === view; //false
});
The only reason this would work with render is because render is forcibly bound to the view by marionette. Any other function would have a different scope. You can change the scope by passing it as the 3rd variable of on, but again then you need to this.model.off in onBeforeDestroy
If you want to call render from modelEvents you have a few options:
modelEvents: {
'change': 'render'
}
//or
modelEvents: function() {
return {
'change': this.render
};
}
// or
modelEvents: {
'change': function() { this.render(); }
}