react-three-game 0.0.84 → 0.0.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,13 +113,35 @@ That means a saved scene is just a prefab, and the same prefab can be:
113
113
  `PrefabRoot` keeps the rendering model narrow and compositional:
114
114
 
115
115
  * `Transform` is the renderer-owned outer transform
116
- * `Geometry` + `Material` become the primary mesh content
116
+ * `Geometry` or `BufferGeometry` + `Material` become the primary mesh content
117
117
  * non-instanced `Model` becomes the node's primary content
118
118
  * `Physics` is a renderer-owned outer wrapper
119
119
  * every other component `View` wraps the current subtree
120
120
 
121
121
  Custom component `View`s use normal React Three Fiber composition with `children`.
122
122
 
123
+ For agent-authored custom meshes, use `BufferGeometry` with flat numeric arrays:
124
+
125
+ ```json
126
+ {
127
+ "id": "triangle",
128
+ "components": {
129
+ "bufferGeometry": {
130
+ "type": "BufferGeometry",
131
+ "properties": {
132
+ "positions": [0, 0, 0, 1, 0, 0, 0, 1, 0],
133
+ "indices": [0, 1, 2],
134
+ "computeVertexNormals": true
135
+ }
136
+ },
137
+ "material": {
138
+ "type": "Material",
139
+ "properties": { "color": "#ff8844" }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
123
145
  ## Prefab Format
124
146
 
125
147
  ```ts
@@ -292,6 +292,7 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
292
292
  }
293
293
  function isRendererHandledComponent(componentType) {
294
294
  return componentType === "Transform"
295
+ || componentType === "BufferGeometry"
295
296
  || componentType === "Geometry"
296
297
  || componentType === "Material"
297
298
  || componentType === "Physics"
@@ -387,21 +388,21 @@ function renderCompositionNode(gameObject, ctx, childNodes) {
387
388
  return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
388
389
  }
389
390
  function renderNodePrimaryContent(gameObject, ctx) {
390
- var _a, _b;
391
- const geometry = findComponent(gameObject, "Geometry");
391
+ var _a, _b, _c;
392
+ const geometry = (_a = findComponent(gameObject, "BufferGeometry")) !== null && _a !== void 0 ? _a : findComponent(gameObject, "Geometry");
392
393
  const material = findComponent(gameObject, "Material");
393
394
  const model = findComponent(gameObject, "Model");
394
395
  const geometryDef = geometry && getComponentDef(geometry.type);
395
396
  const materialDef = material && getComponentDef(material.type);
396
397
  const modelDef = model && getComponentDef(model.type);
397
- const geometryProperties = (_a = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _a !== void 0 ? _a : {};
398
+ const geometryProperties = (_b = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _b !== void 0 ? _b : {};
398
399
  const meshVisible = geometryProperties.visible !== false;
399
400
  const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
400
401
  const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
401
402
  if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
402
403
  return (_jsxs("mesh", { visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
403
404
  }
404
- if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_b = model.properties) === null || _b === void 0 ? void 0 : _b.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
405
+ if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_c = model.properties) === null || _c === void 0 ? void 0 : _c.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
405
406
  return _jsx(modelDef.View, { properties: model.properties });
406
407
  }
407
408
  return null;
@@ -0,0 +1,3 @@
1
+ import { Component } from "./ComponentRegistry";
2
+ declare const BufferGeometryComponent: Component;
3
+ export default BufferGeometryComponent;
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { BooleanField, FieldGroup } from "./Input";
3
+ const DEFAULT_TRIANGLE_POSITIONS = [
4
+ 0, 0, 0,
5
+ 1, 0, 0,
6
+ 0, 1, 0,
7
+ ];
8
+ const DEFAULT_TRIANGLE_INDICES = [0, 1, 2];
9
+ const DEFAULT_TRIANGLE_UVS = [
10
+ 0, 0,
11
+ 1, 0,
12
+ 0, 1,
13
+ ];
14
+ function isFiniteNumberArray(value) {
15
+ return Array.isArray(value) && value.every(entry => typeof entry === 'number' && Number.isFinite(entry));
16
+ }
17
+ function normalizeNumberArray(value, fallback) {
18
+ return isFiniteNumberArray(value) ? value : fallback;
19
+ }
20
+ function toAttributeText(value, fallback) {
21
+ return JSON.stringify(normalizeNumberArray(value, fallback));
22
+ }
23
+ function parseArrayInput(raw) {
24
+ const trimmed = raw.trim();
25
+ if (!trimmed)
26
+ return [];
27
+ const parsed = JSON.parse(trimmed);
28
+ if (!isFiniteNumberArray(parsed)) {
29
+ throw new Error('Expected a JSON array of numbers');
30
+ }
31
+ return parsed;
32
+ }
33
+ function getIndexArray(indices) {
34
+ if (indices.length === 0)
35
+ return null;
36
+ const maxIndex = Math.max(...indices);
37
+ return maxIndex > 65535 ? new Uint32Array(indices) : new Uint16Array(indices);
38
+ }
39
+ function BufferArrayField({ label, value, fallback, onChange, rows = 4, }) {
40
+ return (_jsxs("label", { style: { display: 'grid', gap: 4 }, children: [_jsx("span", { style: { fontSize: '10px', color: '#888', textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 500 }, children: label }), _jsx("textarea", { rows: rows, spellCheck: false, defaultValue: toAttributeText(value, fallback), onBlur: (event) => {
41
+ try {
42
+ onChange(parseArrayInput(event.target.value));
43
+ event.target.setCustomValidity('');
44
+ }
45
+ catch (_a) {
46
+ event.target.setCustomValidity('Expected a JSON array of numbers');
47
+ event.target.reportValidity();
48
+ }
49
+ }, style: {
50
+ width: '100%',
51
+ backgroundColor: '#171717',
52
+ border: '1px solid #333',
53
+ padding: '6px 8px',
54
+ fontSize: '11px',
55
+ color: '#eee',
56
+ fontFamily: 'monospace',
57
+ outline: 'none',
58
+ borderRadius: 3,
59
+ resize: 'vertical',
60
+ boxSizing: 'border-box',
61
+ } })] }));
62
+ }
63
+ function BufferGeometryComponentEditor({ component, onUpdate, }) {
64
+ var _a;
65
+ const properties = (_a = component.properties) !== null && _a !== void 0 ? _a : {};
66
+ return (_jsxs(FieldGroup, { children: [_jsx(BufferArrayField, { label: "Positions", value: properties.positions, fallback: DEFAULT_TRIANGLE_POSITIONS, rows: 5, onChange: (positions) => onUpdate({ positions }) }), _jsx(BufferArrayField, { label: "Indices", value: properties.indices, fallback: DEFAULT_TRIANGLE_INDICES, onChange: (indices) => onUpdate({ indices }) }), _jsx(BufferArrayField, { label: "Normals", value: properties.normals, fallback: [], onChange: (normals) => onUpdate({ normals }) }), _jsx(BufferArrayField, { label: "UVs", value: properties.uvs, fallback: DEFAULT_TRIANGLE_UVS, onChange: (uvs) => onUpdate({ uvs }) }), _jsx(BooleanField, { name: "computeVertexNormals", label: "Compute Normals", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "visible", label: "Visible", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: properties, onChange: onUpdate, fallback: true })] }));
67
+ }
68
+ function BufferGeometryComponentView({ properties }) {
69
+ const positions = normalizeNumberArray(properties.positions, DEFAULT_TRIANGLE_POSITIONS);
70
+ const indices = normalizeNumberArray(properties.indices, DEFAULT_TRIANGLE_INDICES);
71
+ const normals = normalizeNumberArray(properties.normals, []);
72
+ const uvs = normalizeNumberArray(properties.uvs, DEFAULT_TRIANGLE_UVS);
73
+ const indexArray = getIndexArray(indices);
74
+ const hasNormals = normals.length >= 3 && normals.length % 3 === 0;
75
+ const hasUvs = uvs.length >= 2 && uvs.length % 2 === 0;
76
+ return (_jsxs("bufferGeometry", { onUpdate: (geometry) => {
77
+ if (properties.computeVertexNormals !== false && !hasNormals) {
78
+ geometry.computeVertexNormals();
79
+ }
80
+ geometry.computeBoundingBox();
81
+ geometry.computeBoundingSphere();
82
+ }, children: [_jsx("bufferAttribute", { attach: "attributes-position", args: [new Float32Array(positions), 3] }), indexArray ? (_jsx("bufferAttribute", { attach: "index", args: [indexArray, 1] })) : null, hasNormals ? (_jsx("bufferAttribute", { attach: "attributes-normal", args: [new Float32Array(normals), 3] })) : null, hasUvs ? (_jsx("bufferAttribute", { attach: "attributes-uv", args: [new Float32Array(uvs), 2] })) : null] }));
83
+ }
84
+ const BufferGeometryComponent = {
85
+ name: 'BufferGeometry',
86
+ Editor: BufferGeometryComponentEditor,
87
+ View: BufferGeometryComponentView,
88
+ defaultProperties: {
89
+ positions: DEFAULT_TRIANGLE_POSITIONS,
90
+ indices: DEFAULT_TRIANGLE_INDICES,
91
+ normals: [],
92
+ uvs: DEFAULT_TRIANGLE_UVS,
93
+ computeVertexNormals: true,
94
+ },
95
+ };
96
+ export default BufferGeometryComponent;
@@ -15,8 +15,11 @@ export interface MaterialProps extends Omit<MeshStandardMaterialProperties & Mes
15
15
  thickness?: number;
16
16
  ior?: number;
17
17
  texture?: string;
18
+ offset?: [number, number];
18
19
  repeat?: boolean;
19
20
  repeatCount?: [number, number];
21
+ animateOffset?: boolean;
22
+ offsetSpeed?: [number, number];
20
23
  generateMipmaps?: boolean;
21
24
  minFilter?: string;
22
25
  magFilter?: string;
@@ -9,14 +9,19 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { createContext, useContext, useMemo } from 'react';
12
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
13
+ import { createContext, useContext, useMemo, useRef } from 'react';
14
14
  import { extend } from '@react-three/fiber';
15
+ import { useFrame } from '@react-three/fiber';
15
16
  import { FieldRenderer, Label, NumberInput } from './Input';
16
17
  import { useAssetRuntime } from '../runtime';
17
18
  import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
18
19
  import { TexturePicker } from '../../assetviewer/page';
19
20
  import { RepeatWrapping, ClampToEdgeWrapping, SRGBColorSpace, LinearSRGBColorSpace, NearestFilter, LinearFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, FrontSide, BackSide, DoubleSide, } from 'three';
21
+ function Vector2Editor({ label, value, onChange, min, max, step, }) {
22
+ var _a, _b;
23
+ return (_jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsxs("div", { style: { flex: 1 }, children: [_jsxs(Label, { children: [label, " X"] }), _jsx(NumberInput, { value: (_a = value === null || value === void 0 ? void 0 : value[0]) !== null && _a !== void 0 ? _a : 0, onChange: x => { var _a; return onChange([x, (_a = value === null || value === void 0 ? void 0 : value[1]) !== null && _a !== void 0 ? _a : 0]); }, min: min, max: max, step: step, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] }), _jsxs("div", { style: { flex: 1 }, children: [_jsxs(Label, { children: [label, " Y"] }), _jsx(NumberInput, { value: (_b = value === null || value === void 0 ? void 0 : value[1]) !== null && _b !== void 0 ? _b : 0, onChange: y => { var _a; return onChange([(_a = value === null || value === void 0 ? void 0 : value[0]) !== null && _a !== void 0 ? _a : 0, y]); }, min: min, max: max, step: step, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] })] }));
24
+ }
20
25
  const EMPTY_MATERIAL_OVERRIDES = Object.freeze({});
21
26
  const MaterialOverridesContext = createContext(EMPTY_MATERIAL_OVERRIDES);
22
27
  export function useMaterialOverrides() {
@@ -36,6 +41,7 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
36
41
  const materialType = (_a = component.properties.materialType) !== null && _a !== void 0 ? _a : 'standard';
37
42
  const hasTexture = !!component.properties.texture;
38
43
  const hasRepeat = component.properties.repeat;
44
+ const animateOffset = component.properties.animateOffset;
39
45
  const isStandardMaterial = materialType === 'standard';
40
46
  const fields = [
41
47
  {
@@ -82,10 +88,20 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
82
88
  name: 'repeatCount',
83
89
  type: 'custom',
84
90
  label: 'Repeat (X, Y)',
85
- render: ({ value, onChange }) => {
86
- var _a, _b;
87
- return (_jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsxs("div", { style: { flex: 1 }, children: [_jsx(Label, { children: "X" }), _jsx(NumberInput, { value: (_a = value === null || value === void 0 ? void 0 : value[0]) !== null && _a !== void 0 ? _a : 1, onChange: v => { var _a; return onChange([v, (_a = value === null || value === void 0 ? void 0 : value[1]) !== null && _a !== void 0 ? _a : 1]); }, min: 0.01, max: 100, step: 0.1, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] }), _jsxs("div", { style: { flex: 1 }, children: [_jsx(Label, { children: "Y" }), _jsx(NumberInput, { value: (_b = value === null || value === void 0 ? void 0 : value[1]) !== null && _b !== void 0 ? _b : 1, onChange: v => { var _a; return onChange([(_a = value === null || value === void 0 ? void 0 : value[0]) !== null && _a !== void 0 ? _a : 1, v]); }, min: 0.01, max: 100, step: 0.1, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] })] }));
88
- },
91
+ render: ({ value, onChange }) => (_jsx(Vector2Editor, { label: "Repeat", value: value, onChange: onChange, min: 0.01, max: 100, step: 0.1 })),
92
+ }] : []),
93
+ {
94
+ name: 'offset',
95
+ type: 'custom',
96
+ label: 'Offset (X, Y)',
97
+ render: ({ value, onChange }) => (_jsx(Vector2Editor, { label: "Offset", value: value, onChange: onChange, step: 0.01 })),
98
+ },
99
+ { name: 'animateOffset', type: 'boolean', label: 'Animate Offset' },
100
+ ...(animateOffset ? [{
101
+ name: 'offsetSpeed',
102
+ type: 'custom',
103
+ label: 'Speed (X, Y)',
104
+ render: ({ value, onChange }) => (_jsx(Vector2Editor, { label: "Speed", value: value, onChange: onChange, step: 0.01 })),
89
105
  }] : []),
90
106
  {
91
107
  name: 'normalMapTexture',
@@ -97,10 +113,7 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
97
113
  name: 'normalScale',
98
114
  type: 'custom',
99
115
  label: 'Normal Scale (X, Y)',
100
- render: ({ value, onChange }) => {
101
- var _a, _b;
102
- return (_jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsxs("div", { style: { flex: 1 }, children: [_jsx(Label, { children: "X" }), _jsx(NumberInput, { value: (_a = value === null || value === void 0 ? void 0 : value[0]) !== null && _a !== void 0 ? _a : 1, onChange: v => { var _a; return onChange([v, (_a = value === null || value === void 0 ? void 0 : value[1]) !== null && _a !== void 0 ? _a : 1]); }, min: 0, max: 5, step: 0.01, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] }), _jsxs("div", { style: { flex: 1 }, children: [_jsx(Label, { children: "Y" }), _jsx(NumberInput, { value: (_b = value === null || value === void 0 ? void 0 : value[1]) !== null && _b !== void 0 ? _b : 1, onChange: v => { var _a; return onChange([(_a = value === null || value === void 0 ? void 0 : value[0]) !== null && _a !== void 0 ? _a : 1, v]); }, min: 0, max: 5, step: 0.01, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] })] }));
103
- },
116
+ render: ({ value, onChange }) => (_jsx(Vector2Editor, { label: "Normal", value: value, onChange: onChange, min: 0, max: 5, step: 0.01 })),
104
117
  }] : []),
105
118
  { name: 'generateMipmaps', type: 'boolean', label: 'Generate Mipmaps' },
106
119
  {
@@ -131,13 +144,16 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
131
144
  }
132
145
  // View for Material component
133
146
  function MaterialComponentView({ properties: rawProps }) {
134
- var _a, _b, _c, _d, _e, _f;
147
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
135
148
  const { getTexture } = useAssetRuntime();
136
149
  const properties = rawProps;
137
150
  const materialType = (_a = properties === null || properties === void 0 ? void 0 : properties.materialType) !== null && _a !== void 0 ? _a : 'standard';
138
151
  const textureName = properties === null || properties === void 0 ? void 0 : properties.texture;
152
+ const offset = properties === null || properties === void 0 ? void 0 : properties.offset;
139
153
  const repeat = properties === null || properties === void 0 ? void 0 : properties.repeat;
140
154
  const repeatCount = properties === null || properties === void 0 ? void 0 : properties.repeatCount;
155
+ const animateOffset = properties === null || properties === void 0 ? void 0 : properties.animateOffset;
156
+ const offsetSpeed = properties === null || properties === void 0 ? void 0 : properties.offsetSpeed;
141
157
  const generateMipmaps = (properties === null || properties === void 0 ? void 0 : properties.generateMipmaps) !== false;
142
158
  const minFilter = (properties === null || properties === void 0 ? void 0 : properties.minFilter) || 'LinearMipmapLinearFilter';
143
159
  const magFilter = (properties === null || properties === void 0 ? void 0 : properties.magFilter) || 'LinearFilter';
@@ -147,7 +163,7 @@ function MaterialComponentView({ properties: rawProps }) {
147
163
  const normalMapTexture = normalMapTextureName ? (_c = getTexture(normalMapTextureName)) !== null && _c !== void 0 ? _c : undefined : undefined;
148
164
  const materialSource = properties !== null && properties !== void 0 ? properties : {};
149
165
  // Destructure all material props and separate custom texture handling props
150
- const { texture: _texture, repeat: _repeat, repeatCount: _repeatCount, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "repeat", "repeatCount", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "side"]);
166
+ const { texture: _texture, offset: _offset, repeat: _repeat, repeatCount: _repeatCount, animateOffset: _animateOffset, offsetSpeed: _offsetSpeed, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "offset", "repeat", "repeatCount", "animateOffset", "offsetSpeed", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "side"]);
151
167
  const sideMap = { FrontSide, BackSide, DoubleSide };
152
168
  const resolvedSide = sideProp ? ((_d = sideMap[sideProp]) !== null && _d !== void 0 ? _d : FrontSide) : FrontSide;
153
169
  const minFilterMap = {
@@ -162,8 +178,9 @@ function MaterialComponentView({ properties: rawProps }) {
162
178
  NearestFilter,
163
179
  LinearFilter
164
180
  };
181
+ const animatedOffsetRef = useRef([(_e = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _e !== void 0 ? _e : 0, (_f = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _f !== void 0 ? _f : 0]);
165
182
  const finalTexture = useMemo(() => {
166
- var _a, _b;
183
+ var _a, _b, _c, _d;
167
184
  if (!texture)
168
185
  return undefined;
169
186
  const t = texture.clone();
@@ -176,13 +193,24 @@ function MaterialComponentView({ properties: rawProps }) {
176
193
  t.wrapS = t.wrapT = ClampToEdgeWrapping;
177
194
  t.repeat.set(1, 1);
178
195
  }
196
+ t.offset.set((_a = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _a !== void 0 ? _a : 0, (_b = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _b !== void 0 ? _b : 0);
179
197
  t.colorSpace = SRGBColorSpace;
180
198
  t.generateMipmaps = generateMipmaps;
181
- t.minFilter = (_a = minFilterMap[minFilter]) !== null && _a !== void 0 ? _a : LinearMipmapLinearFilter;
182
- t.magFilter = (_b = magFilterMap[magFilter]) !== null && _b !== void 0 ? _b : LinearFilter;
199
+ t.minFilter = (_c = minFilterMap[minFilter]) !== null && _c !== void 0 ? _c : LinearMipmapLinearFilter;
200
+ t.magFilter = (_d = magFilterMap[magFilter]) !== null && _d !== void 0 ? _d : LinearFilter;
183
201
  t.needsUpdate = true;
184
202
  return t;
185
- }, [texture, repeat, repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0], repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1], generateMipmaps, minFilter, magFilter]);
203
+ }, [texture, repeat, repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0], repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1], offset === null || offset === void 0 ? void 0 : offset[0], offset === null || offset === void 0 ? void 0 : offset[1], generateMipmaps, minFilter, magFilter]);
204
+ animatedOffsetRef.current = [(_g = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _g !== void 0 ? _g : 0, (_h = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _h !== void 0 ? _h : 0];
205
+ useFrame((_, delta) => {
206
+ var _a, _b;
207
+ if (!finalTexture || !animateOffset)
208
+ return;
209
+ const nextX = animatedOffsetRef.current[0] + ((_a = offsetSpeed === null || offsetSpeed === void 0 ? void 0 : offsetSpeed[0]) !== null && _a !== void 0 ? _a : 0) * delta;
210
+ const nextY = animatedOffsetRef.current[1] + ((_b = offsetSpeed === null || offsetSpeed === void 0 ? void 0 : offsetSpeed[1]) !== null && _b !== void 0 ? _b : 0) * delta;
211
+ animatedOffsetRef.current = [nextX, nextY];
212
+ finalTexture.offset.set(nextX, nextY);
213
+ });
186
214
  const finalNormalMap = useMemo(() => {
187
215
  if (!normalMapTexture)
188
216
  return undefined;
@@ -199,7 +227,7 @@ function MaterialComponentView({ properties: rawProps }) {
199
227
  if (materialType === 'basic') {
200
228
  return _jsx("meshBasicNodeMaterial", Object.assign({}, sharedProps));
201
229
  }
202
- return (_jsx("meshStandardNodeMaterial", Object.assign({}, sharedProps, { normalMap: finalNormalMap, normalScale: finalNormalMap ? [(_e = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[0]) !== null && _e !== void 0 ? _e : 1, (_f = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[1]) !== null && _f !== void 0 ? _f : 1] : undefined })));
230
+ return (_jsx("meshStandardNodeMaterial", Object.assign({}, sharedProps, { normalMap: finalNormalMap, normalScale: finalNormalMap ? [(_j = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[0]) !== null && _j !== void 0 ? _j : 1, (_k = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[1]) !== null && _k !== void 0 ? _k : 1] : undefined })));
203
231
  }
204
232
  const MaterialComponent = {
205
233
  name: 'Material',
@@ -212,6 +240,9 @@ const MaterialComponent = {
212
240
  wireframe: false,
213
241
  transparent: false,
214
242
  opacity: 1,
243
+ offset: [0, 0],
244
+ animateOffset: false,
245
+ offsetSpeed: [0, 0],
215
246
  metalness: 0,
216
247
  roughness: 1
217
248
  },
@@ -1,28 +1,30 @@
1
- import GeometryComponent from './GeometryComponent';
2
1
  import TransformComponent from './TransformComponent';
2
+ import GeometryComponent from './GeometryComponent';
3
+ import BufferGeometryComponent from './BufferGeometryComponent';
4
+ import ModelComponent from './ModelComponent';
5
+ import TextComponent from './TextComponent';
3
6
  import MaterialComponent from './MaterialComponent';
4
7
  import PhysicsComponent from './PhysicsComponent';
5
8
  import SpotLightComponent from './SpotLightComponent';
6
9
  import PointLightComponent from './PointLightComponent';
7
10
  import DirectionalLightComponent from './DirectionalLightComponent';
8
11
  import AmbientLightComponent from './AmbientLightComponent';
9
- import ModelComponent from './ModelComponent';
10
- import TextComponent from './TextComponent';
11
12
  import EnvironmentComponent from './EnvironmentComponent';
12
13
  import CameraComponent from './CameraComponent';
13
14
  import ClickComponent from './ClickComponent';
14
15
  import SoundComponent from './SoundComponent';
15
16
  export const builtinComponents = [
16
- GeometryComponent,
17
17
  TransformComponent,
18
+ GeometryComponent,
19
+ BufferGeometryComponent,
20
+ ModelComponent,
21
+ TextComponent,
18
22
  MaterialComponent,
19
23
  PhysicsComponent,
20
24
  SpotLightComponent,
21
25
  PointLightComponent,
22
26
  DirectionalLightComponent,
23
27
  AmbientLightComponent,
24
- ModelComponent,
25
- TextComponent,
26
28
  EnvironmentComponent,
27
29
  CameraComponent,
28
30
  ClickComponent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "description": "high performance 3D game engine built in React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",