Having issues loading glsl shaders in a Next.js project. More specifically, when attempting to load the glslify function 'snoise3', I am prompted with the following error message:
WARNING: 0:64: 'glslify' : unrecognized pragma ERROR: 0:73: 'snoise3' : no matching overloaded function found
I've seem similar issues around the internet but can't seem to track a solution that works in my project.
Here's the CodeSandbox I am modeling off of: https://codesandbox.io/s/r3f-wavey-image-shader-j4uwl?file=/src/App.js
For reference, here's my three.js object code (including glsl code):
import * as THREE from "three";
import React, { useRef, Suspense } from "react";
import { Canvas, extend, useFrame, useLoader } from "#react-three/fiber";
import { shaderMaterial } from "#react-three/drei";
const glslify = require("glslify");
const WaveShaderMaterial = shaderMaterial(
// Uniform
{
uTime: 0,
uColor: new THREE.Color(0.0, 0.0, 0.0),
uTexture: new THREE.Texture(),
},
// Vertex Shader
glslify`precision mediump float;
varying vec2 vUv;
varying float vWave;
uniform float uTime;
#pragma glslify: snoise3 = require(glsl-noise/simplex/3d.glsl);
void main() {
vUv = uv;
vec3 pos = position;
float noiseFreq = 2.0;
float noiseAmp = 0.4;
vec3 noisePos = vec3(pos.x * noiseFreq + uTime, pos.y, pos.z);
pos.z += snoise3(noisePos) * noiseAmp;
vWave = pos.z;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}`,
// Fragment Shader
glslify`precision mediump float;
uniform vec3 uColor;
uniform float uTime;
uniform sampler2D uTexture;
varying vec2 vUv;
varying float vWave;
void main() {
float wave = vWave * 0.2;
vec3 texture = texture2D(uTexture, vUv + wave).rgb;
gl_FragColor = vec4(texture, 1.0);
}`
);
extend({ WaveShaderMaterial });
const Wave = () => {
const ref = useRef();
useFrame(({ clock }) => (ref.current.uTime = clock.getElapsedTime()));
const [image] = useLoader(THREE.TextureLoader, [
"https://i.imgur.com/Fc5Rwr6.png",
]);
return (
<mesh>
<planeBufferGeometry args={[0.5, 0.5, 16, 16]} />
<waveShaderMaterial
wireframe
uColor={"orange"}
ref={ref}
uTexture={image}
GLSLVersion={THREE.GLSL3}
/>
</mesh>
);
};
const GridWaveObject = () => {
return (
<Canvas camera={{ fov: 12, position: [1, 3, 5] }}>
<Suspense fallback={null}>
<Wave />
</Suspense>
</Canvas>
);
};
export default GridWaveObject;
and also my next.config.js:
/** #type {import('next').NextConfig} */
const webpack = require("webpack");
const nextConfig = {
reactStrictMode: true,
webpack(config) {
// Perform customizations to webpack config
config.module.rules.push({
// shader import support
test: /\.glsl$/,
use: [
{
loader: "ify-loader",
options: {
name: "dist/[path][name].[ext]",
},
},
"webpack-glsl-loader",
],
});
return config;
},
};
module.exports = nextConfig;
I originally tried just using the babel configuration, but this failed pretty quick because I'm using next/font. I've also tried using all different types of loaders (glsl-loader, ify-loader, webpack-glsl-loader, and more), extracting the glsl code to their own files and importing, as well as all different types of next configs. I'm stumped here as I believe I've tried everything I can.
If anyone has experience using shaders with #react-three/fiber or #react-three/drei with Next.js, I'd appreciate any solution that can help me move forward :)
Related
I have an a-frame entity with a component which is generating a pointcloud made of points in the init function but i am unable to access the points geometry
I had a demo working just using ThreeJS :
for ( let i = 0; i < mainContainer.children.length; i ++ ) {
const object = mainContainer.children[ i ];
if ( object instanceof THREE.Points ) {
I have set the pointCloud on entity
el.setObject3D('pointCloud', new THREE.Points( this.geometry, this.pointMaterial ));
i cannot access the geometry in the update function of the component, either using el.getObject3D('pointCloud') or using the id of the entity
As you set the object3D with setObject3D('pointCloud', new THREE.Points( ));, getObject3D('pointCloud') will retrieve the created THREE.Points instance.
The geometry is an attribute of the THREE.Points object:
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("cloud", {
init: function() {
// create the points
const vertices = [];
for (let i = 0; i < 500; i++) {
const x = THREE.MathUtils.randFloatSpread(5);
const y = THREE.MathUtils.randFloatSpread(5);
const z = THREE.MathUtils.randFloatSpread(5);
vertices.push(x, y, z);
}
// create a geometry from the points
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
// small grey points material
const material = new THREE.PointsMaterial({ color: 0x888888, size: 0.05 });
// setObject3D sets the points under the name 'cloud'
this.el.setObject3D("cloud", new THREE.Points(geometry, material))
},
update: function() {
// retrieve the THREE.Points object
const points = this.el.getObject3D("cloud");
// get it's geometry attribute
const geometry = points.geometry;
// log it
console.log(geometry)
}
})
</script>
<a-scene>
<a-entity position="0 1 -4" cloud></a-entity>
</a-scene>
I have several historical maps (around 8kx8k) that I display with a vue3 component. the image and the width and height are passed to these components.
<MapFlex :imgurl="content.maps[5].karte" :width="width" :height="height"></MapFlex>
for this I used the ol-source-image-static
that works quite well so far. The maps are more square, the area for the card is a landscape format rectangle. I now want to show this map fully visible when opened. that works if I don't set an extent in the view.
when I use the extent [-width/2, -height/2, width/2, height/2] I can no longer move the image out of the display field. but I can't make it smaller either. the map fills the view area.
if I want to change the extent rect dynamically depending on the zoom, there is a feedback loop, the system becomes lame and at some point it also stops.
What I actually want to do is that I switch between different extents when changing the zoom.
Or is there a function with which I would like to dynamically generate the appropriate extent. It is important that the map is visible in a smaller size in the display area and can then be zoomed in without being able to slide the map out of the area.
<template>
<div class="map-container">
<ol-map :loadTilesWhileAnimating="true" style="height:980px" >
<ol-view ref="view" :center="center" :rotation="rotation" :zoom="zoom" :maxZoom="maxZoom" :minZoom="minZoom" :projection="projection" :extent="extent" #zoomChanged="onZoomChanged" #resolutionChanged="onResolutionChanged"/>
<ol-image-layer>
<ol-source-image-static :url="imgUrl" :imageSize="size" :imageExtent="imageExtent" :projection="projection"></ol-source-image-static>
</ol-image-layer>
</ol-map>
</div>
</template>
<script>
import { ref, reactive} from 'vue'
export default {
props: {
imgurl: {
type: String,
default: "http://localhost:8080/karten/map-1.jpg",
},
width: {
type: Number,
default: 1024
},
height: {
type: Number,
default: 968
}
},
setup(props) {
const zoom = ref(2)
const minZoom = ref(1)
const maxZoom = ref(6)
const rotation = ref(0)
const faktor = 2
const size = ref([props.width, props.height])
const center = ref( [size.value[0]/2, size.value[1]/2] ); // ref ([0,0]) //
const imageExtent = ref( [-1*props.width/2, -1*props.height/2 , props.width/2, props.height/2 ] )
const extent = ref( [-1 * props.width / faktor, -1 * props.height / faktor, props.width / faktor, props.height / faktor] ) // number[leftBottomX, leftBottomY, rightTopX, rightTopY]
const currentZoom = ref(zoom.value);
const emitter = useEmitter();
console.log(extent);
const projection = reactive({
code: 'xkcd-image',
units: 'pixels',
extent: extent,
const onZoomChanged = (val) => {
currentZoom.value = val;
if (val < 3){
extent.value = [-1 * props.width / 1.5, -1 * props.height / 1.5, props.width /1.5, props.height / 1.5]
}
extent.value = [-1 * props.breite / 1.2, -1 * props.hoehe / 1.2, props.breite / 1.2, props.hoehe / 1.2]
}
const onResolutionChanged = () => {
// console.log("onResolutionChanged");
}
return {
center,
projection,
zoom,
minZoom,
maxZoom,
rotation,
size,
imageExtent,
extent,
imgUrl,
onZoomChanged,
onResolutionChanged,
}
},
}
</script>
I am using the marker from current example but with vertical oriented chart and a few my upgrades. So my problem that in case of vertical chart the labels and values of variables isn't shown. But the same logic is properly works with horizontal oriented chart.
private createCustomMarker(): void {
if (!this.seriesInstances.length) return;
const resultTable: UIElementColumn<UIBackground> = this.chartInstance
.addUIElement(UILayoutBuilders.Column, {
x: this.chartInstance.getDefaultAxisX(),
y: this.chartInstance.getDefaultAxisY()
})
.setMouseInteractions(false)
.setOrigin(UIOrigins.LeftCenter)
.setMargin(5);
const datetimeRow: UITextBox<UIBackground> = resultTable
.addElement(UILayoutBuilders.Row)
.addElement(UIElementBuilders.TextBox);
const rowsY: UITextBox<UIBackground>[] = this.seriesInstances
.map((el: ISeriesInstance, i: number) => {
return resultTable
.addElement(UILayoutBuilders.Row)
.addElement(UIElementBuilders.TextBox)
.setTextFillStyle(this.seriesInstances[i].instance.getStrokeStyle().getFillStyle());
});
const tick: CustomTick = (this.isAppearanceHorizontal ? this.chartInstance.getDefaultAxisX() : this.chartInstance.getDefaultAxisY())
.addCustomTick()
.setAllocatesAxisSpace(false)
.disposeMarker();
// Hide custom cursor components initially.
resultTable.dispose();
tick.dispose();
this.chartInstance.onSeriesBackgroundMouseMove((_: ChartXY<PointMarker, UIBackground>, event: MouseEvent): void => {
const mouseLocationClient: { x: number; y: number } = { x: event.clientX, y: event.clientY };
const mouseLocationEngine: Point = this.chartInstance.engine.clientLocation2Engine(
mouseLocationClient.x,
mouseLocationClient.y
);
// Translate mouse location to LCJS coordinate system for solving data points from series, and translating to Axes.
// Translate mouse location to Axis.
const mouseLocationAxis: Point = translatePoint(
mouseLocationEngine,
this.chartInstance.engine.scale,
this.seriesInstances[0].instance.scale
);
// Solve the nearest data point to the mouse on each series.
const nearestDataPoints: CursorPoint<Series2D>[] = this.seriesInstances.map((el: ISeriesInstance) => {
return el.instance.solveNearestFromScreen(mouseLocationEngine) // on this line the most of elements have undefined, but data for it exists and poits are near beetween each other
});
// console.log(nearestDataPoints);
// Find the nearest solved data point to the mouse.
const nearestPoint: CursorPoint<Series2D> = nearestDataPoints.reduce((prev: CursorPoint<Series2D>, curr: CursorPoint<Series2D>) => {
if (!prev) return curr;
if (!curr) return prev;
if (this.isAppearanceHorizontal) {
return Math.abs(mouseLocationAxis.y - curr.location.y) < Math.abs(mouseLocationAxis.y - prev.location.y) ? curr : prev;
} else {
return Math.abs(mouseLocationAxis.x - curr.location.x) < Math.abs(mouseLocationAxis.x - prev.location.x) ? curr : prev
}
});
if (nearestPoint) {
// Set custom cursor location.
resultTable.setPosition({
x: mouseLocationAxis.x,
y: mouseLocationAxis.y,
});
// Change origin of result table based on cursor location.
let resultTableOrigin;
const yScale: number = this.chartInstance.engine.scale.y.getInnerInterval();
const isResultTableOriginXRight: boolean = mouseLocationEngine.x > this.chartInstance.engine.scale.x.getInnerInterval() / 2;
if (mouseLocationEngine.y > yScale - (yScale / 100 * 30)) { // mouseLocationEngine.y > yScale - 30%
resultTableOrigin = isResultTableOriginXRight ? UIOrigins.RightTop : UIOrigins.LeftTop;
} else if (mouseLocationEngine.y < yScale / 100 * 30) { // mouseLocationEngine.y > 30% of yScale
resultTableOrigin = isResultTableOriginXRight ? UIOrigins.RightBottom : UIOrigins.LeftBottom;
} else {
resultTableOrigin = isResultTableOriginXRight ? UIOrigins.RightCenter : UIOrigins.LeftCenter;
}
resultTable.setOrigin(resultTableOrigin);
// Format result table text.
const datetimeValue = this.isAppearanceHorizontal
? this.chartInstance.getDefaultAxisX().formatValue(nearestPoint.location.x)
: this.chartInstance.getDefaultAxisY().formatValue(nearestPoint.location.y)
datetimeRow.setText(`${datetimeValue}`);
rowsY.map((rowY: UITextBox<UIBackground>, i: number) => {
// this.seriesInstances[i].instance.isDisposed() ? rowY.dispose() : rowY.restore(); after this line labels of the table is low font contrast
if (nearestDataPoints[i]?.location) {
const foundSeries = chain(this.track.series)
.flatMap()
.value()[i]
const value: string = this.isAppearanceHorizontal
? this.chartInstance.getDefaultAxisY().formatValue(nearestDataPoints[i].location.y)
: this.chartInstance.getDefaultAxisX().formatValue(nearestDataPoints[i].location.x)
rowY.setText(`${this.seriesInstances[i].instance.getName()}: ${value} ${foundSeries.unit}`) // probleblem on this line
}
});
tick.setValue(
this.isAppearanceHorizontal
? nearestPoint.location.x
: nearestPoint.location.y
);
resultTable.restore();
tick.restore();
} else {
resultTable.dispose();
tick.dispose();
}
});
this.chartInstance.onSeriesBackgroundMouseLeave(() => {
resultTable.dispose();
tick.dispose();
});
this.chartInstance.onSeriesBackgroundMouseDragStart(() => {
resultTable.dispose();
tick.dispose();
});
}
I want to understand why almost the same code don't work on vertical oriented chart, but on horizontal works good.
I'm attempting to generate a safelist for TailWindCSS 3.0.23 using a function so I can cover some ranges.
For some reason, the classes still seem to be purged.
module.exports = {
content: ['./app/**/*.php', './resources/**/*.{php,vue,js}'],
safelist: function(){
let list = [
'user-administrator:not(.wp-admin)',
];
for(let i = 0; i <= 100; i += 5 ){
list[list.length] = 'opacity-' + i;
}
return list;
},
Is this type of thing possible? Any ideas?
You could the following code in javascript.
const tailwindColors = require("./node_modules/tailwindcss/colors")
const colorSafeList = []
// Skip these to avoid a load of deprecated warnings when tailwind starts up
const deprecated = ["lightBlue", "warmGray", "trueGray", "coolGray", "blueGray"]
for (const colorName in tailwindColors) {
if (deprecated.includes(colorName)) {
continue
}
// Define all of your desired shades
const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]
const pallette = tailwindColors[colorName]
if (typeof pallette === "object") {
shades.forEach((shade) => {
if (shade in pallette) {
// colorSafeList.push(`text-${colorName}-${shade}`) <-- You can add different colored text as well
colorSafeList.push(`bg-${colorName}-${shade}`)
}
})
}
}
// tailwind.config.js
module.exports = {
safelist: colorSafeList, // <-- add the safelist here
content: ["{pages,app}/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: tailwindColors,
},
},
plugins: [],
}
I ended up solving this by adding a pair of parenthesis after the closing bracket of the function so it executes.
module.exports = {
content: ['./app/**/*.php', './resources/**/*.{php,vue,js}'],
safelist: function(){
let list = [
'user-administrator:not(.wp-admin)',
];
for(let i = 0; i <= 100; i += 5 ){
list[list.length] = 'opacity-' + i;
}
return list;
}(),
Hi all trying to write some logic in paper.js (also using opentype.js for font data) so that when a number contains two or more consecutive zeros' the zero path is divided so that it is solid.
Things i know a zero path, using my particular font, is made up of an outer path with 19 segments and an inner path made up of 18 segments
So I thought would try to iterate over all paths check if a path has 19 segments and the next path has 18 segments and call path.unite() which kind of works. But I only want it to do this with consecutive '0' eg '100', '1000' but not 10.
So i was trying to do an if else statment where if-else (the current path has 18 segments and the next path is less than 18 segments) if true then do nothin or call path.divide()?
Im sure there is a way better way of doing this. Can you help please.
link to codepen
paper.install(window);
window.onload = () => {
paper.setup("canvas");
opentype.load(
"https://assets.codepen.io/1070/pphatton-ultralight-webfont.woff",
(err, font) => {
if (err) {
console.log(err);
} else {
const fontPath = font.getPath("10k", 0, 100, 100).toSVG();
const count = new paper.CompoundPath(fontPath);
count.unite();
count.children.forEach((child, i) => {
if (
child.segments.length === 19 &&
count.children[i + 1].segments.length === 18
) {
const eye = child.unite();
eye.selected = true;
} else if(
count.children[i + 1].segments.length === 18
&& child.segments.length < 18
) {
console.log('hello');
// const target = child.divide();
count.children[i].fillColor = 'black'
} else{
}
});
// const flatCount = count.children[1].unite()
// console.log(count.children[2].segments.length)
// const flatCountTwo = count.children[5].unite()
// flatCount.translate(5,0)
count.fillColor = "red";
project.activeLayer.fitBounds(view.bounds.scale(0.6));
}
}
);
};
I think that rather than using Font.getPath to retrieve a single svg path for the whole text, you should use Font.getPaths to retrieve an svg path for each character.
This way you can quite simply do your analysis on the input string directly and handle the consecutive 0 differently than other characters.
Edit
In order to detect the consecutive zeros, yes, you could use a regex or loop over the characters, like I did in the following example.
Here's a fiddle showcasing a possible solution.
const handleZero = (path) => {
path.children = path.children.slice(0, 1);
};
const getConsecutiveZerosIndices = (content) => {
const zero = '0';
return [...content]
.map((char, i) => ({ char, i }))
.filter(({ char, i }) => {
const previousCharacter = content?.[i - 1];
const nextCharacter = content?.[i + 1];
return char === zero && (previousCharacter === zero || nextCharacter === zero);
})
.map(({ i }) => i);
};
const drawText = (content, font) => {
const fontPaths = font.getPaths(content, 0, 100, 100);
const consecutiveZerosIndices = getConsecutiveZerosIndices(content);
const paths = fontPaths.map((fontPath, i) => {
const path = new paper.CompoundPath(fontPath.toSVG());
if (consecutiveZerosIndices.includes(i)) {
handleZero(path);
}
return path;
});
const group = new paper.Group(paths);
group.fillColor = 'red';
return group;
};
const draw = (font) => {
const path1 = drawText('10k', font);
const path2 = drawText('100k', font);
const path3 = drawText('1000k', font);
path2.position = path1.position.add(0, path1.bounds.height * 1.2);
path3.position = path2.position.add(0, path2.bounds.height * 1.2);
paper.project.activeLayer.fitBounds(paper.view.bounds.scale(0.6));
};
paper.setup('canvas');
opentype.load(
'https://assets.codepen.io/1070/pphatton-ultralight-webfont.woff',
(err, font) => draw(font)
);