fix: bug on textute vegetation item
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useRef } from "react";
|
|||||||
import { useGLTF } from "@react-three/drei";
|
import { useGLTF } from "@react-three/drei";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { mergeBufferGeometries } from "three-stdlib";
|
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";
|
||||||
import type { Vector3Tuple } from "@/types/three/three";
|
import type { Vector3Tuple } from "@/types/three/three";
|
||||||
import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene";
|
import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene";
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ function createMergedMeshes(scene: THREE.Group): MergedMeshData[] {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const geometry = mergeBufferGeometries(group.geometries, false);
|
const geometry = mergeGeometries(group.geometries, false);
|
||||||
|
|
||||||
for (const sourceGeometry of group.geometries) {
|
for (const sourceGeometry of group.geometries) {
|
||||||
sourceGeometry.dispose();
|
sourceGeometry.dispose();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export const GRASS_CONFIG = {
|
|||||||
patchSize: 30,
|
patchSize: 30,
|
||||||
bladeCount: 32000,
|
bladeCount: 32000,
|
||||||
bladeWidth: 0.08,
|
bladeWidth: 0.08,
|
||||||
maxBladeHeight: 0.56,
|
maxBladeHeight: 0.67,
|
||||||
randomHeightAmount: 0.25,
|
randomHeightAmount: 0.25,
|
||||||
surfaceOffset: 0.025,
|
surfaceOffset: 0.025,
|
||||||
heightTextureSize: 128,
|
heightTextureSize: 128,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useEffect, useMemo, useRef } from "react";
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useGLTF } from "@react-three/drei";
|
import { useGLTF } from "@react-three/drei";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { mergeBufferGeometries } from "three-stdlib";
|
|
||||||
import {
|
import {
|
||||||
normalizeMapScale,
|
normalizeMapScale,
|
||||||
useTerrainHeightSampler,
|
useTerrainHeightSampler,
|
||||||
@@ -23,11 +22,6 @@ interface MeshData {
|
|||||||
material: THREE.Material | THREE.Material[];
|
material: THREE.Material | THREE.Material[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MeshMergeGroup {
|
|
||||||
geometries: THREE.BufferGeometry[];
|
|
||||||
material: THREE.Material | THREE.Material[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const meshDataCache = new Map<string, MeshData[]>();
|
const meshDataCache = new Map<string, MeshData[]>();
|
||||||
|
|
||||||
function cloneMaterial(
|
function cloneMaterial(
|
||||||
@@ -38,46 +32,29 @@ function cloneMaterial(
|
|||||||
: material.clone();
|
: material.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disposeMaterialOnly(
|
|
||||||
material: THREE.Material | THREE.Material[],
|
|
||||||
): void {
|
|
||||||
if (Array.isArray(material)) {
|
|
||||||
for (const item of material) {
|
|
||||||
item.dispose();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
material.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
function disposeInstancedMapMesh(mesh: THREE.InstancedMesh): void {
|
function disposeInstancedMapMesh(mesh: THREE.InstancedMesh): void {
|
||||||
mesh.dispose();
|
mesh.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGeometrySignature(geometry: THREE.BufferGeometry): string {
|
function hasFinitePositions(geometry: THREE.BufferGeometry): boolean {
|
||||||
const attributes = Object.entries(geometry.attributes)
|
const position = geometry.getAttribute("position");
|
||||||
.map(([name, attribute]) => {
|
if (!position) return false;
|
||||||
return `${name}:${attribute.itemSize}:${attribute.normalized}`;
|
|
||||||
})
|
|
||||||
.sort()
|
|
||||||
.join("|");
|
|
||||||
|
|
||||||
return `${geometry.index ? "indexed" : "non-indexed"}:${attributes}`;
|
for (let index = 0; index < position.count; index++) {
|
||||||
}
|
if (
|
||||||
|
!Number.isFinite(position.getX(index)) ||
|
||||||
function createMaterialKey(
|
!Number.isFinite(position.getY(index)) ||
|
||||||
material: THREE.Material | THREE.Material[],
|
!Number.isFinite(position.getZ(index))
|
||||||
): string {
|
) {
|
||||||
if (Array.isArray(material)) {
|
return false;
|
||||||
return material.map((item) => item.uuid).join("|");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return material.uuid;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractMeshes(scene: THREE.Group): MeshData[] {
|
function extractMeshes(scene: THREE.Group): MeshData[] {
|
||||||
const groups = new Map<string, MeshMergeGroup>();
|
const meshes: MeshData[] = [];
|
||||||
|
|
||||||
scene.updateMatrixWorld(true);
|
scene.updateMatrixWorld(true);
|
||||||
scene.traverse((child) => {
|
scene.traverse((child) => {
|
||||||
@@ -85,50 +62,18 @@ function extractMeshes(scene: THREE.Group): MeshData[] {
|
|||||||
|
|
||||||
const geometry = child.geometry.clone();
|
const geometry = child.geometry.clone();
|
||||||
geometry.applyMatrix4(child.matrixWorld);
|
geometry.applyMatrix4(child.matrixWorld);
|
||||||
const material = child.material;
|
if (!hasFinitePositions(geometry)) {
|
||||||
const key = `${createMaterialKey(material)}:${createGeometrySignature(geometry)}`;
|
geometry.dispose();
|
||||||
const group = groups.get(key);
|
|
||||||
|
|
||||||
if (group) {
|
|
||||||
group.geometries.push(geometry);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.set(key, {
|
meshes.push({
|
||||||
geometries: [geometry],
|
geometry,
|
||||||
material: cloneMaterial(material),
|
material: cloneMaterial(child.material),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return [...groups.values()]
|
return meshes;
|
||||||
.map((group) => {
|
|
||||||
if (group.geometries.length === 1) {
|
|
||||||
const [geometry] = group.geometries;
|
|
||||||
if (!geometry) return null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
geometry,
|
|
||||||
material: group.material,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedGeometry = mergeBufferGeometries(group.geometries, false);
|
|
||||||
|
|
||||||
for (const geometry of group.geometries) {
|
|
||||||
geometry.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mergedGeometry) {
|
|
||||||
disposeMaterialOnly(group.material);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
geometry: mergedGeometry,
|
|
||||||
material: group.material,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter((meshData): meshData is MeshData => meshData !== null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInstanceMatrices(
|
function setInstanceMatrices(
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useEffect, useMemo, useRef } from "react";
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useGLTF } from "@react-three/drei";
|
import { useGLTF } from "@react-three/drei";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { mergeBufferGeometries } from "three-stdlib";
|
|
||||||
import { useTerrainHeightSampler } from "@/hooks/three/useTerrainHeight";
|
import { useTerrainHeightSampler } from "@/hooks/three/useTerrainHeight";
|
||||||
import type { VegetationInstance } from "@/types/map/mapScene";
|
import type { VegetationInstance } from "@/types/map/mapScene";
|
||||||
import { useWind } from "@/hooks/world/useWind";
|
import { useWind } from "@/hooks/world/useWind";
|
||||||
@@ -38,6 +37,7 @@ interface VegetationWindUniforms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const meshDataCache = new Map<string, MeshData[]>();
|
const meshDataCache = new Map<string, MeshData[]>();
|
||||||
|
const VEGETATION_ALPHA_TEST = 0.35;
|
||||||
|
|
||||||
function updateVegetationWindUniforms(
|
function updateVegetationWindUniforms(
|
||||||
uniforms: VegetationWindUniforms,
|
uniforms: VegetationWindUniforms,
|
||||||
@@ -90,6 +90,15 @@ function applyVegetationWindMaterial(
|
|||||||
};
|
};
|
||||||
|
|
||||||
windMaterial.userData.windUniforms = windUniforms;
|
windMaterial.userData.windUniforms = windUniforms;
|
||||||
|
windMaterial.alphaTest = Math.max(
|
||||||
|
windMaterial.alphaTest,
|
||||||
|
VEGETATION_ALPHA_TEST,
|
||||||
|
);
|
||||||
|
windMaterial.transparent = false;
|
||||||
|
windMaterial.depthTest = true;
|
||||||
|
windMaterial.depthWrite = true;
|
||||||
|
windMaterial.side = THREE.DoubleSide;
|
||||||
|
windMaterial.needsUpdate = true;
|
||||||
|
|
||||||
windMaterial.onBeforeCompile = (shader) => {
|
windMaterial.onBeforeCompile = (shader) => {
|
||||||
shader.uniforms.uVegetationWindTime = windUniforms.time;
|
shader.uniforms.uVegetationWindTime = windUniforms.time;
|
||||||
@@ -130,11 +139,25 @@ function applyVegetationWindMaterial(
|
|||||||
return windMaterial;
|
return windMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasFinitePositions(geometry: THREE.BufferGeometry): boolean {
|
||||||
|
const position = geometry.getAttribute("position");
|
||||||
|
if (!position) return false;
|
||||||
|
|
||||||
|
for (let index = 0; index < position.count; index++) {
|
||||||
|
if (
|
||||||
|
!Number.isFinite(position.getX(index)) ||
|
||||||
|
!Number.isFinite(position.getY(index)) ||
|
||||||
|
!Number.isFinite(position.getZ(index))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function extractMeshes(scene: THREE.Group): MeshData[] {
|
function extractMeshes(scene: THREE.Group): MeshData[] {
|
||||||
const meshesByMaterial = new Map<
|
const meshes: MeshData[] = [];
|
||||||
string,
|
|
||||||
{ geometries: THREE.BufferGeometry[]; material: THREE.Material }
|
|
||||||
>();
|
|
||||||
scene.updateMatrixWorld(true);
|
scene.updateMatrixWorld(true);
|
||||||
|
|
||||||
scene.traverse((child) => {
|
scene.traverse((child) => {
|
||||||
@@ -147,41 +170,19 @@ function extractMeshes(scene: THREE.Group): MeshData[] {
|
|||||||
|
|
||||||
const geometry = child.geometry.clone();
|
const geometry = child.geometry.clone();
|
||||||
geometry.applyMatrix4(child.matrixWorld);
|
geometry.applyMatrix4(child.matrixWorld);
|
||||||
|
if (!hasFinitePositions(geometry)) {
|
||||||
const existing = meshesByMaterial.get(material.uuid);
|
geometry.dispose();
|
||||||
if (existing) {
|
return;
|
||||||
existing.geometries.push(geometry);
|
|
||||||
} else {
|
|
||||||
meshesByMaterial.set(material.uuid, {
|
|
||||||
geometries: [geometry],
|
|
||||||
material: material.clone(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
addWindWeightAttribute(geometry);
|
||||||
|
|
||||||
|
meshes.push({
|
||||||
|
geometry,
|
||||||
|
material: applyVegetationWindMaterial(material.clone()),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return [...meshesByMaterial.values()]
|
return meshes;
|
||||||
.map(({ geometries, material }) => {
|
|
||||||
const mergedGeometry = mergeBufferGeometries(geometries, false);
|
|
||||||
|
|
||||||
for (const geometry of geometries) {
|
|
||||||
if (geometry !== mergedGeometry) {
|
|
||||||
geometry.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mergedGeometry) {
|
|
||||||
material.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
addWindWeightAttribute(mergedGeometry);
|
|
||||||
|
|
||||||
return {
|
|
||||||
geometry: mergedGeometry,
|
|
||||||
material: applyVegetationWindMaterial(material),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter((meshData): meshData is MeshData => meshData !== null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInstanceMatrices(
|
function createInstanceMatrices(
|
||||||
@@ -194,18 +195,17 @@ function createInstanceMatrices(
|
|||||||
const position = new THREE.Vector3();
|
const position = new THREE.Vector3();
|
||||||
const rotation = new THREE.Euler();
|
const rotation = new THREE.Euler();
|
||||||
const quaternion = new THREE.Quaternion();
|
const quaternion = new THREE.Quaternion();
|
||||||
const scale = new THREE.Vector3();
|
const scale = new THREE.Vector3(
|
||||||
|
scaleMultiplier,
|
||||||
|
scaleMultiplier,
|
||||||
|
scaleMultiplier,
|
||||||
|
);
|
||||||
|
|
||||||
for (const instance of instances) {
|
for (const instance of instances) {
|
||||||
const matrix = new THREE.Matrix4();
|
const matrix = new THREE.Matrix4();
|
||||||
|
|
||||||
position.set(...instance.position);
|
position.set(...instance.position);
|
||||||
scale.set(
|
position.y += -geometryBottomY * scaleMultiplier;
|
||||||
instance.scale[0] * scaleMultiplier,
|
|
||||||
instance.scale[1] * scaleMultiplier,
|
|
||||||
instance.scale[2] * scaleMultiplier,
|
|
||||||
);
|
|
||||||
position.y += -geometryBottomY * scale.y;
|
|
||||||
rotation.set(
|
rotation.set(
|
||||||
instance.rotation[0] + rotationOffset[0],
|
instance.rotation[0] + rotationOffset[0],
|
||||||
instance.rotation[1] + rotationOffset[1],
|
instance.rotation[1] + rotationOffset[1],
|
||||||
|
|||||||
Reference in New Issue
Block a user