pmndrs / react-ogl Goto Github PK
View Code? Open in Web Editor NEW🦴 A barebones react renderer for ogl.
Home Page: https://npmjs.com/react-ogl
License: MIT License
🦴 A barebones react renderer for ogl.
Home Page: https://npmjs.com/react-ogl
License: MIT License
Would you like me to setup CI like maath? sandboxes in demo/src/sandboxes that can be used for dev ( I do it with a router in the demo ) and deployed to CSB CI for testing/examples
eg. https://github.com/pmndrs/maath/tree/main/demo/src/sandboxes/points -> 📦 Codesandbox
This would also get deployed at CI with the package built from the PR (like we do in r3f, drei & leva too)
useLoader
is an important feature in R3F, it would be very useful here too.
I can't run npm install
anymore once I installed react-ogl in a clean Next project due to conflicting dependencies between React 18 and React ^17. What am I missing?
NPM version: 8.18.0
Node version: 16.15.0
The log:
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR! peer react@"^17.0.2 || ^18.0.0-0" from [email protected]
npm ERR! node_modules/next
npm ERR! next@"12.2.5" from the root project
npm ERR! peer react@"^18.2.0" from [email protected]
npm ERR! node_modules/react-dom
npm ERR! peer react-dom@"^17.0.2 || ^18.0.0-0" from [email protected]
npm ERR! node_modules/next
npm ERR! next@"12.2.5" from the root project
npm ERR! peerOptional react-dom@">=17.0" from [email protected]
npm ERR! node_modules/react-ogl
npm ERR! react-ogl@"^0.6.4" from the root project
npm ERR! 2 more (react-use-measure, the root project)
npm ERR! 8 more (react-ogl, react-use-measure, styled-jsx, suspend-react, ...)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"17.0.1" from [email protected]
npm ERR! node_modules/react-native
npm ERR! peer react-native@">=0.64.0-rc.0 || 0.0.0-*" from @react-native-community/[email protected]
npm ERR! node_modules/react-native/node_modules/@react-native-community/cli
npm ERR! @react-native-community/cli@"^5.0.1-alpha.1" from [email protected]
npm ERR! peerOptional react-native@">=0.64" from [email protected]
npm ERR! node_modules/react-ogl
npm ERR! react-ogl@"^0.6.4" from the root project
npm ERR! 1 more (@expo/browser-polyfill)
npm ERR!
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/react
npm ERR! peer react@"17.0.1" from [email protected]
npm ERR! node_modules/react-native
npm ERR! peer react-native@">=0.64.0-rc.0 || 0.0.0-*" from @react-native-community/[email protected]
npm ERR! node_modules/react-native/node_modules/@react-native-community/cli
npm ERR! @react-native-community/cli@"^5.0.1-alpha.1" from [email protected]
npm ERR! peerOptional react-native@">=0.64" from [email protected]
npm ERR! node_modules/react-ogl
npm ERR! react-ogl@"^0.6.4" from the root project
npm ERR! 1 more (@expo/browser-polyfill)
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See /Users/arno/.npm/eresolve-report.txt for a full report.
package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "12.2.5",
"ogl": "^0.0.97",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-ogl": "^0.6.4"
},
"devDependencies": {
"@types/node": "18.7.13",
"@types/react": "18.0.17",
"@types/react-dom": "18.0.6",
"eslint": "8.22.0",
"eslint-config-next": "12.2.5",
"typescript": "4.7.4"
}
}
useLoader passes url as url
but TextureLoader only accepts src
Line 126 in 075f082
urls.map(async (url: string) => {
// @ts-ignore OGL's loaders don't have a consistent signature
if (classExtends(loader, OGL.TextureLoader)) return loader.load(gl, { url })
return await loader.load(gl, url)
}),
Follow this:
https://github.com/oframe/ogl/blob/master/src/core/Transform.js#L39
parent reference will be invalid this:
https://github.com/pmndrs/react-ogl/blob/main/src/reconciler.ts#L88
Then this drops attached
via 'attach' node reference that not will reattached to new instance.
Follow props signature, we able to use a custom renderer, but it not make sense because we can't pass canvas reference, because canvas not exist when we create it.
https://github.com/pmndrs/react-ogl/blob/main/src/shared/utils.ts#L105
Need to allow use a functional argument of rendere that will return instance for allowing this:
React18 had remove {}
from ReactFragment type and now refs types for react-ogl down, because really it not a React.Node
17:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/v17/index.d.ts#L232
18
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L228:
When trying to build in next.js I am getting this error:
./node_modules/react-ogl/dist/reconciler.mjs
Attempted import error: 'unstable_act' is not exported from 'react' (imported as 'React').
Seems like the issue is coming from this in reconciler.ts
:
export const act: Act = 'unstable_act' in React ? (React as any).unstable_act : (React as any).act
I don't have experience with testing in react, so I would really appreciate your help but let me know if I can help you debug this in any way.
Dispose is required because we can't know when React kill node or only replace it.
For mesh this is critical, because Geometry allocating GPU memory, Textures too, and other GL objects.
Suggest add to this:
https://github.com/pmndrs/react-ogl/blob/main/src/reconciler.ts#L121
Or remove call:
https://github.com/oframe/ogl/blob/master/src/core/Program.js#L208
https://github.com/oframe/ogl/blob/master/src/core/Geometry.js#L266
Or dispose
call with nullish check, dispose
or destroy
is convential, and can be implemented in subclasses.
Sample:
import { createRef, useRef, useState } from "react";
import { useFrame, Canvas } from "react-ogl/web";
import { render } from "react-dom";
import { Mesh, Renderer } from "ogl";
const Box = (props) => {
const mesh = useRef<Mesh>();
const [hovered, setHover] = useState(false);
const [active, setActive] = useState(false);
return (
<mesh
{...props}
ref={mesh}
scale={active ? 1.5 : 1}
onClick={() => setActive((value) => !value)}
onPointerOver={() => setHover(true)}
onPointerOut={() => setHover(false)}
>
<plane {...props} />
<program
vertex={`
attribute vec3 position;
attribute vec3 normal;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
varying vec3 vNormal;
void main() {
vNormal = normalize(normalMatrix * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`}
fragment={`
precision highp float;
uniform vec3 uColor;
varying vec3 vNormal;
void main() {
vec3 normal = normalize(vNormal);
float lighting = dot(normal, normalize(vec3(10)));
gl_FragColor.rgb = uColor + lighting * 0.1;
gl_FragColor.a = 1.0;
}
`}
uniforms={{ uColor: hovered ? "hotpink" : "orange" }}
/>
</mesh>
);
};
const ref = createRef<HTMLCanvasElement>();
render(
<Canvas
camera={{ position: [0, 1.6, 8] }}
ref={ref}
renderer={() =>
new Renderer ({
canvas: ref.current,
dpr: 2,
antialias: true,
autoClear: true,
})
}
>
<transform position={[0, 1.6, 0]}>
<Box key="grid" width={4} height={2} />
<transform position={[-2, 0, 0]} rotation={[0, Math.PI / 6, 0]}>
<Box
key="left"
position={[-0.5, 0, 0]}
width={1}
height={2}
/>
</transform>
<transform position={[2, 0, 0]} rotation={[0, -Math.PI / 6, 0]}>
<Box
key="right"
position={[0.5, 0, 0]}
width={1}
height={2}
/>
</transform>
</transform>
</Canvas>,
document.getElementById("react-content")
);
Expected:
Paneles will be hover and color changed
Actual:
Paneles not react
Bug in this place:
Line 95 in 7d98001
when you use a special material with internal state and custom uniforms, react will produce errors because default uniforms will be dropped.
This is critical, because witout this will not working a special components for program with attach!
for ex:
import { Program } from "ogl";
import { extend, Node } from "react-ogl";
import React from "react";
const FRAG = `
precision highp float;
uniform vec3 uColor;
uniform vec2 uPoint;
uniform sampler2D uSampler0;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(uSampler0, vUv);
gl_FragColor = mix (vec4(1.0, 0., 0., 1.), gl_FragColor, step(0.01, length(uPoint - vUv)));
}
`;
const VERT = `
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vNormal = normalize(normalMatrix * normal);
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
export interface IProgProps {
mousePoint?: Array<number>;
color?: string | any;
children: any[];
}
export class BaseProgramWithTexture extends Program {
constructor(gl: GLContext) {
super(gl, {
vertex: VERT,
fragment: FRAG,
transparent: true,
uniforms:{
uColor: { value: [1,1,1] },
uPoint: { value: [0,0] },
uSampler0: { value: null },
}
});
}
set texture(v) {
this.uniforms.uSampler0 = {value: v};
}
get texture() {
return this.uniforms.uSampler0?.value;
}
}
extend({BaseProgramWithTexture});
declare global {
namespace JSX {
interface IntrinsicElements {
baseProgramWithTexture: Node<BaseProgramWithTexture, typeof BaseProgramWithTexture>
}
}
}
export default React.forwardRef<Program, IProgProps>(
({ color = "pink", mousePoint = [0, 0], children }, ref) => {
return (
<baseProgramWithTexture
ref={ref}
uniforms = {{
uColor: color
}}
>
<ChessTexture/>
</baseProgramWithTexture>
);
}
);
// ChessTexture.tsx
// which should apply texture to `texture` field that is setter
export default ({width = 256, height = 256, step = 64}) => {
return React.createElement('texture', {
attach: 'texture',
image: generateCheckmate(width, height, step)
})
}
//programRef is reference on to our baseProgramWithTexture and uPoint MUST BE AVAILABLE
const handleMove = ({ hit }: any) => {
(
programRef.current.uniforms as Record<string, { value: any }>
).uPoint.value = hit?.uv || [0, 0];
};
And if we try to change uPoint manually - will crash, because reconciler remove required fields.
Critical Bug in 0.0.3
See:
https://github.com/pmndrs/react-ogl/blob/main/src/utils.ts#L84
There are not a __r3f
field in child because there are not a place where it is assigned.
This is down when we copy node with attachments.
Then this place not required because we remove a __previousAttach
above
https://github.com/pmndrs/react-ogl/blob/main/src/utils.ts#L86
Now, ogl-react resolve only prebuilded classes from
https://github.com/pmndrs/react-ogl/blob/main/src/constants.ts#L7
But for some case need has a custom class that not extends from listed, and which requires a pass gl to args.
atm this resolved as hack:
So, maybe we can check a static field to classes that required to construct with GL and will check it in this:
https://github.com/pmndrs/react-ogl/blob/main/src/reconciler.ts#L35
like :
if (!object && elem.requireGL || GL_ELEMENTS.some((elem) => Object.prototype.isPrototypeOf.call(elem, target) || elem === target)) {
}
Issues occured when custom node has field or accesor with on
start.
Like:
class CustomClass {
onSomeCallback = null
}
//....
<CustomClassNode onSomeCallback = {() => {}} />
For this case reconciller will move onSomeCallback
to _handlers and field will be never assigned
https://github.com/pmndrs/react-ogl/blob/main/src/utils.ts#L36
https://github.com/pmndrs/react-ogl/blob/main/src/utils.ts#L109
Some js native classes have a set
method but not have a copy
.
Map/Set/TypedArray (UInt8Array for example).
Ok, there are not reason handle a Map/Set as props because there are not API what use it, but should be way pass ArrayBuffer view.
This line try to use copy method that wrong for this case:
https://github.com/pmndrs/react-ogl/blob/main/src/utils.ts#L144
Possible fix:
// we should bypass set for array buffer, because it not resizable. You cant set a 4 values to array of 0.
if (target?.set && !ArrayBuffer.isView(value)) {
if (target.constructor.name === value.constructor.name && target.copy) {
target.copy(value)
} else if (Array.isArray(value)) {
target.set(...value)
} else {
// Support shorthand scalar syntax like scale={1}
const scalar = new Array(target.length).fill(value)
target.set(...scalar)
}
} else {
This sometime can be used for API's like a :
{ /* texture 2x2 filled to red */ }
<texture
width={2}
height={2}
image={new Uint8Array([255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255]}
/>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.