Adding instanced mesh react fiber threejs - javascript

I'm making a project where I need to generate pretty huge map. I need to generate a lot of ground blocks with trees or rocks, kinda similar to
minecraft (each block must be clickable). But I have a problem with optimization. I tried cutting render distance, adding fog, and playing
with three instances (I'm using threejs fiber library for react). Could anyone help me explain how instances work and tell me how to apply
them to my project? Hre's the code to generate it in normal way, without instancing.
import React, { Suspense } from "react";
import { useEffect, useState } from "react";
import Box from "./Box";
export default function Ground({
layout,
selectBox,
selectedBox,
position,
}) {
const [boxArray, setBoxArray] = useState([]);
const size = 20;
let id = 0;
useEffect(() => {
const boxArrayCopy = [];
for (let i = -(size / 2); i < size / 2; i++) {
let hasTree = Math.random() > 0.99;
for (let j = -(size / 2); j < size / 2; j++) {
const layoutObject = layout.filter(
(layoutObject) => layoutObject.x === i && layoutObject.y === j
);
const box = (
<Box
key={id}
coords={{ x: j * 5, y: 0, z: i * 5 }}
color={layoutObject.length > 0 ? layoutObject[0].color : 0x61892f}
selectBox={selectBox}
selectedBox={selectedBox}
hasTree={hasTree}
hasStone={Math.random() < 0.2}
// just a random condition to add tree to box
/>
);
boxArrayCopy.push(box);
id++;
if (hasTree) hasTree = Math.random() > 0.5;
}
setBoxArray([...boxArrayCopy]);
}
console.log("GROUND DONE");
}, []);
return (
<group position={position ?? [0, 0, 0]}>
<Suspense fallback={null}>{boxArray}</Suspense>
</group>
);
}

Related

P5 — Fixed Random positions with an animated GIF

I am having trouble with a sketch that uses animated GIF. I want to setup a random layout that can be modified with a mouse press. This layout then should play the animated give on the given coordinates by the initial setup. The issue is that these positions keep being changed by the drawn and I cannot figure it out how to have the random coordinates and the give playing on those.
How can I set up this random layout and play the gif on the set coordinates? Thank you in advance.
Pseudo-code
Setup multiple random coordinates for the layout
Animated the GIF within the inial layout coordinates
Press mouse to randomise the layout
Sketch
CodeSanBox
Code
import "./styles.css";
import p5 from "p5";
// img = sketch.loadImage(`http://placeimg.com/640/360/people`);
let myp5 = new p5((sketch) => {
var img;
var settings;
var src_x,
src_y,
src_width,
src_height,
dist_x,
dist_y,
dist_width,
dist_height,
dist_scale,
step;
let count = 25;
p5.disableFriendlyErrors = true;
var layout;
sketch.preload = () => {
// img = sketch.loadImage("https://source.unsplash.com/random");
img = sketch.loadImage("../assets/02.gif");
// img = sketch.createImg("../assets/02.gif");
};
sketch.setup = () => {
sketch.createCanvas(sketch.int(img.width), sketch.int(img.height));
sketch.angleMode(sketch.DEGREES);
for (let i = 0; i < count; i++) {
makeRandomn(img);
}
// createLayout(img);
};
function makeRandomn(gif) {
sketch.background(gif);
for (let i = 0; i < count; i++) {
src_width = sketch.random(gif.width / 50, gif.width / 5);
src_height = sketch.random(gif.height / 50, gif.height / 5);
src_x = sketch.random(0, gif.width - src_width);
src_y = sketch.random(0, gif.height - src_height);
step = sketch.random(gif.width / 100, gif.width / 20);
dist_x = src_x + sketch.random(-step, step);
dist_y = src_y + sketch.random(-step, step);
dist_scale = sketch.random(0.5, 1.5);
dist_width = src_width * dist_scale;
dist_height = src_height * dist_scale;
let gif_trim = gif.get(src_x, src_y, src_width, src_height);
sketch.push();
sketch.translate(dist_x + dist_width / 2, dist_y + dist_height / 2);
sketch.rotate(sketch.random(90) * (sketch.random(100) > 50 ? -1 : 1));
sketch.imageMode(sketch.CENTER);
sketch.image(gif_trim, 0, 0, dist_width, dist_height);
sketch.image(gif_trim, 0, 0);
sketch.pop();
}
// sketch.noLoop();
settings = {
src_x: src_x,
src_y: src_y,
src_width: src_width,
src_height: src_height,
dist_x: dist_x,
dist_y: dist_y,
dist_width: dist_width,
dist_height: dist_height
};
}
function createLayout(gif) {
sketch.background(gif);
for (let i = 0; i < count; i++) {
const {
src_x,
src_y,
src_width,
src_height,
dist_x,
dist_y,
dist_width,
dist_height
} = settings;
let gif_trim = gif.get(src_x, src_y, src_width, src_height);
sketch.push();
sketch.translate(dist_x + dist_width / 2, dist_y + dist_height / 2);
sketch.rotate(sketch.random(90) * (sketch.random(100) > 50 ? -1 : 1));
sketch.imageMode(sketch.CENTER);
sketch.image(gif_trim, 0, 0, dist_width, dist_height);
sketch.image(gif_trim, 0, 0);
sketch.pop();
}
// sketch.noLoop();
}
sketch.draw = () => {
// createLayout(img);
makeRandomn(img);
};
sketch.mousePressed = () => {
console.log(settings);
};
});
// let myp5 = new p5(s, "p5sketch");
It looks like the problem is draw and mousePressed. Draw is filled with makeRandom( ); So every frame a new random location is picked. If you click and settings is called, the only thing that happens is the coordinates are reassigned. But draw is run again and the mouse click is essentially passed over. You want instead for makeRandom to be called on a mouse press.

Endless wave animation with paper.js and reactJS. TypeError: Cannot read property 'point' of undefined

I'm trying to create an endlessly waving line (animation) the same way it's done here http://paperjs.org/tutorials/animation/creating-animations/
with paper.js & React JS.
But I get the error
"TypeError: Cannot read property 'point' of undefined."
whenever I try to loop over the segments inside the path.
Funny thing is, that, If I don't loop, I don't get this error with 'point' property and everything works perfectly fine (I see the animation).
import React from 'react';
import './Canvas.css';
import Paper, { Path, Point, onFrame } from "paper";
const Wdivider = Math.round(window.innerWidth / 6);
const linesNumber = Math.round(window.innerHeight / 10);
const points = [[0, 0], [Wdivider, 25], [2*Wdivider, 0], [3*Wdivider, 25], [4*Wdivider, 0], [5*Wdivider, 25],[window.innerWidth, 0]];
var topLeft = [0, window.innerHeight/2];
var bottomRight = [window.innerWidth, window.innerHeight/2];
let myPath;
let copy;
class Canvas extends React.Component{
componentDidMount() {
this.createLines();
Paper.view.onFrame = event => {
this.onFrameAnim(event, myPath);
};
}
createLines(){
var myCanvas = document.getElementById("myCanvas");
Paper.setup(myCanvas);
myPath = new Path({segments: points});
myPath.smooth();
}
//Animation
onFrameAnim(event, path) {
for (let i=0; i < myPath.length; i++ ) {
if (myPath.segments[i].point.y >= 0) {
myPath.segments[i].point.y -= 50;
}
else {
myPath.segments[i].point.y += 50;
}
}
}
render(){
return (
<canvas id="myCanvas" resize="true" />
);
}
}
export default Canvas;

How to reduce a React.js component's size when creating it from a higher level

I am using an html canvas in React.js to draw a picture.
I get the dimensions (in millimeters) for rectangles and paths through an api call.
Rather than passing down scaling variables to reduce the dimensions before drawing the canvas, I would like to draw the canvas assuming the dimensions are in pixels, and to then, when the component is created through a higher level call, reduce its size (i.e. scale it down to a fixed width, BUT keep the aspect ratio).
How can I do this?
Below some code to demonstrate. I use withStyles also, but I've excluded this from the sample code for the sake of brevity.
I have a CanvasComponent class:
import React from 'react'
class CanvasComponent extends React.Component {
componentDidMount() {
this.updateCanvas();
}
updateCanvas() {
const {drawing_objects, drawing, classes} = this.props
const w = drawing.dims[0]
const h = drawing.dims[1]
const ctx = this.refs.canvas.getContext('2d');
ctx.fillStyle = "#79d279"
ctx.fillRect(0,0, w, h);
let i;
let r;
for (i = 0; i < drawing_objects.length; i++) {
r = drawing_objects[i]
ctx.beginPath();
...
}
}
render() {
const {drawing_objects, drawing, classes} = this.props
const w = drawing.dims[0]
const h = drawing.dims[1]
return (
<canvas ref="canvas" width={w} height={h}/>
);
}
}
export default CanvasComponent
And when I create it from a higher level, I do something like:
import React from 'react'
import CanvasComponent from "../components/CanvasComponent"
class Index extends React.Component {
render(){
const {drawing, drawing_objects} = this.props
return
<div >
<CanvasComponent
drawing={drawing}
drawing_objects={drawing_objects}
/>
</div>
}
}

P5 with react at 60 FPS

To get P5 to work with React, I am using the P5Wrapper import.
I got a simple starfield animation to work on my tile, but the performance is an issue. The animation slows to a crawl at 512 "star" objects, so I scaled it back to 128. However, even at 128, the FPS seems much too low, averaging below 30 FPS. I am looking for ways to speed up P5's performance in React so that the animations can run closer to 60 FPS.
P5 code:
function sketch (p) {
const star = () => {
const x = p.random(-TILE_SIZE/2, TILE_SIZE/2)
const y = p.random(-TILE_SIZE/2, TILE_SIZE/2)
const z = p.random(TILE_SIZE)
return { x, y, z }
}
const stars = new Array(128)
p.setup = () => {
p.createCanvas(TILE_SIZE, TILE_SIZE)
for (let i = 0; i < stars.length; i++) {
stars[i] = star()
}
}
const update = (coord) => {
const { z } = coord
let newZ = z - 8
if (newZ < 1) {
newZ = p.random(TILE_SIZE)
}
return { ...coord, z: newZ }
}
const show = (coord) => {
const { x, y, z } = coord
p.fill(255)
p.noStroke()
const sx = p.map(x / z, 0, 1, 0, TILE_SIZE)
const sy = p.map(y / z, 0, 1, 0, TILE_SIZE)
const r = p.map(z, 0, TILE_SIZE, 4, 0)
p.ellipse(sx, sy, r, r)
}
p.draw = () => {
p.background(0)
p.translate(TILE_SIZE/2, TILE_SIZE/2)
for (let i = 0; i < stars.length; i++) {
stars[i] = update(stars[i])
show(stars[i])
}
}
}
How P5Wrapper is used:
import P5Wrapper from 'react-p5-wrapper'
...
render (
<ItemContainer key={uuidv4()}>
<header>
{name}
<p>{description}</p>
</header>
<P5Wrapper sketch={sketch} />
</ItemContainer>
)
How the starfield tile actually looks (2 tiles).
I am planning to add more animations depending on performance. Or switching to SVG.
Resolved the frame-rate issue without changing the actual animation logic. There could still be plenty of performance optimization that is still needed.
I noticed that the animation gets progressively slower as I un-mount and re-mount the component. Digging into the Github issue results in this post about performance degradation. The poster provided a PR and commit that was never merged and the repository haven't been updated for over a year.
Instead, it's better to remove the package and just create your own component following the poster's update:
import React from 'react'
import p5 from 'p5'
export default class P5Wrapper extends React.Component {
componentDidMount() {
this.canvas = new p5(this.props.sketch, this.wrapper)
if( this.canvas.myCustomRedrawAccordingToNewPropsHandler ) {
this.canvas.myCustomRedrawAccordingToNewPropsHandler(this.props)
}
}
componentWillReceiveProps(newprops) {
if(this.props.sketch !== newprops.sketch){
this.canvas.remove()
this.canvas = new p5(newprops.sketch, this.wrapper)
}
if( this.canvas.myCustomRedrawAccordingToNewPropsHandler ) {
this.canvas.myCustomRedrawAccordingToNewPropsHandler(newprops)
}
}
componentWillUnmount() {
this.canvas.remove()
}
render() {
return <div ref={wrapper => this.wrapper = wrapper}></div>
}
}
This at the very least resolved the performance degradation issue for mounting and unmounting the component. Additionally, my frames have jumped from sub 30 to nearly 60 FPS. This could be because I also imported the latest P5 package since I no longer rely on react-p5-wrapper to do the imports.

Unable to render Canvas due to TypeError: Cannot read property 'getImageData' of null

I am trying to get a react application to render the conways life method. I am still a bit new to React.js and was hoping someone could shed some light on why my code is broken and also how to fix it. And perhaps how to prevent it from happening in the future. Below is my code
import React, { Component } from 'react';
import Life from './life';
import './App.css';
/**
* Life canvas
*/
class LifeCanvas extends Component {
/**
* Constructor
*/
constructor(props) {
super(props);
this.life = new Life(props.width, props.height);
this.life.randomize();
}
/**
* Component did mount
*/
componentDidMount() {
requestAnimationFrame(() => {
this.animFrame();
});
}
/**
* Handle an animation frame
*/
animFrame() {
//
// !!!! IMPLEMENT ME !!!!
//
let width = this.props.width;
let height = this.props.height;
// Request another animation frame
// Update life and get cells
let cells = this.life.getCells();
// Get canvas framebuffer, a packed RGBA array
let canvas = this.refs.canvas;
let ctx = canvas.getContext('2D');
let imageData = ctx.getImageData(0, 0, width, height);
// Convert the cell values into white or black for the canvas
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let index = (y * width + x) * 4;
let lifeStatus = cells[y][x];
let color = lifeStatus === 0 ? 0xff : 0x00;
imageData.data[index + 0] = color; // R
imageData.data[index + 1] = color; // G
imageData.data[index + 1] = color; // B
imageData.data[index + 1] = color; // A
}
}
// Put the new image data back on the canvas
ctx.putImageData(imageData, 0, 0);
// Next generation of life
this.life.step();
requestAnimationFrame(() => {
this.animFrame();
});
}
/**
* Render
*/
render() {
return (
<canvas
ref="canvas"
width={this.props.width}
height={this.props.height}
/>
);
}
}
/**
* Life holder component
*/
class LifeApp extends Component {
/**
* Render
*/
render() {
return (
<div>
<LifeCanvas width={600} height={600} />
</div>
);
}
}
/**
* Outer App component
*/
class App extends Component {
/**
* Render
*/
render() {
return (
<div className="App">
<LifeApp />
</div>
);
}
}
export default App;
When it renders the page the error in the subject line is produced in big red letters. I am not sure what it means. Please help.
It is saying ctx is null. getContext() will return null when the passed context type is not valid. In your case you passed 2D the proper context type string is 2d
var c = document.createElement('canvas');
var ctx = c.getContext('2D');
console.log("ctx is: ",ctx);
ctx = c.getContext('2d');
console.log("ctx is now: ",ctx);

Categories