grass
This commit is contained in:
parent
4aa2dd0237
commit
dd9c35d3bd
BIN
assets/terrain/decoration/flowers01.png
Normal file
BIN
assets/terrain/decoration/flowers01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
assets/terrain/decoration/flowers02.png
Normal file
BIN
assets/terrain/decoration/flowers02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
BIN
assets/terrain/decoration/grass01.png
Normal file
BIN
assets/terrain/decoration/grass01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
BIN
assets/terrain/decoration/grass02.png
Normal file
BIN
assets/terrain/decoration/grass02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
@ -1,5 +1,5 @@
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { Color } from 'three';
|
||||
import { Color, DoubleSide, MeshBasicMaterial, Vector3 } from 'three';
|
||||
import { isMobileOrTablet } from '../common/helper';
|
||||
import { CharacterPacket, CompositePacket } from '../common/types/packet';
|
||||
import { IcyNetUser } from '../common/types/user';
|
||||
@ -16,6 +16,9 @@ import { ClientWorld } from './object/world/ClientWorld';
|
||||
import { ClientWorldLoader } from './object/world/ClientWorldLoader';
|
||||
import { ClientWorldManifest } from './object/world/ClientWorldManifest';
|
||||
import { Renderer } from './renderer';
|
||||
import { to2D } from '../common/convert';
|
||||
import { Grass } from './object/model/grass';
|
||||
import { BaseTexture } from './object/resource/texture';
|
||||
|
||||
export class Game {
|
||||
public players: (Player | PlayerEntity)[] = [];
|
||||
@ -77,6 +80,71 @@ export class Game {
|
||||
|
||||
this.renderer.scene.add(this.world.world);
|
||||
this.renderer.scene.background = cube.texture;
|
||||
|
||||
// test
|
||||
const grasstex = await BaseTexture.load(
|
||||
'/assets/terrain/decoration/grass01.png',
|
||||
);
|
||||
const flowertex = await BaseTexture.load(
|
||||
'/assets/terrain/decoration/flowers02.png',
|
||||
);
|
||||
const flowertex2 = await BaseTexture.load(
|
||||
'/assets/terrain/decoration/flowers01.png',
|
||||
);
|
||||
const grassfield = Grass.getInstance().createGrassPatch(
|
||||
new Vector3(10, 0, 10),
|
||||
8,
|
||||
0.5,
|
||||
8,
|
||||
this.world.getInterpolatedHeight.bind(this.world),
|
||||
);
|
||||
|
||||
const flowerfield = Grass.getInstance().createGrassPatch(
|
||||
new Vector3(10, 0, 10),
|
||||
8,
|
||||
4,
|
||||
3,
|
||||
this.world.getInterpolatedHeight.bind(this.world),
|
||||
);
|
||||
|
||||
const flowerfield2 = Grass.getInstance().createGrassPatch(
|
||||
new Vector3(8, 0, 8),
|
||||
4,
|
||||
4,
|
||||
3,
|
||||
this.world.getInterpolatedHeight.bind(this.world),
|
||||
);
|
||||
|
||||
const grass = Grass.getInstance().createInstance(
|
||||
grassfield,
|
||||
new MeshBasicMaterial({
|
||||
side: DoubleSide,
|
||||
map: grasstex.texture,
|
||||
alphaTest: 0.7,
|
||||
}),
|
||||
);
|
||||
|
||||
const flowers = Grass.getInstance().createInstance(
|
||||
flowerfield,
|
||||
new MeshBasicMaterial({
|
||||
side: DoubleSide,
|
||||
map: flowertex.texture,
|
||||
alphaTest: 0.7,
|
||||
}),
|
||||
);
|
||||
|
||||
const flowers2 = Grass.getInstance().createInstance(
|
||||
flowerfield2,
|
||||
new MeshBasicMaterial({
|
||||
side: DoubleSide,
|
||||
map: flowertex2.texture,
|
||||
alphaTest: 0.7,
|
||||
}),
|
||||
);
|
||||
|
||||
this.renderer.scene.add(grass);
|
||||
this.renderer.scene.add(flowers);
|
||||
this.renderer.scene.add(flowers2);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
|
97
src/client/object/model/grass.ts
Normal file
97
src/client/object/model/grass.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
InstancedMesh,
|
||||
Material,
|
||||
Matrix4,
|
||||
Quaternion,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from 'three';
|
||||
import { rand } from '../../../common/helper';
|
||||
|
||||
let instance: Grass;
|
||||
export class Grass {
|
||||
private geom!: BufferGeometry;
|
||||
initialize() {
|
||||
const grassindices = [0, 1, 2, 2, 1, 3, 4, 5, 6, 6, 5, 7];
|
||||
const grassuvs = [0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0];
|
||||
const grassverts = [
|
||||
-0.25, 0.5, 0, -0.25, 0, 0, 0.25, 0.5, 0, 0.25, 0, 0, 0, 0.5, -0.25, 0, 0,
|
||||
-0.25, 0, 0.5, 0.25, 0, 0, 0.25,
|
||||
];
|
||||
|
||||
const grassnormals = Array.from({ length: grassverts.length }, (_, k) => {
|
||||
return k % 2 === 0 ? 1 : 0;
|
||||
});
|
||||
|
||||
this.geom = new BufferGeometry();
|
||||
this.geom.setIndex(grassindices);
|
||||
this.geom.setAttribute(
|
||||
'position',
|
||||
new Float32BufferAttribute(grassverts, 3),
|
||||
);
|
||||
this.geom.setAttribute(
|
||||
'normal',
|
||||
new Float32BufferAttribute(grassnormals, 3),
|
||||
);
|
||||
this.geom.setAttribute('uv', new Float32BufferAttribute(grassuvs, 2));
|
||||
}
|
||||
|
||||
public static getInstance(): Grass {
|
||||
if (!instance) {
|
||||
instance = new Grass();
|
||||
instance.initialize();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public createInstance(
|
||||
positions: Vector3[],
|
||||
material: Material,
|
||||
): InstancedMesh {
|
||||
const mesh = new InstancedMesh(this.geom, material, positions.length);
|
||||
positions.forEach((pos, index) => {
|
||||
const mat = new Matrix4();
|
||||
const quat = new Quaternion();
|
||||
quat.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI * Math.random());
|
||||
mat.compose(pos, quat, new Vector3(1, 1, 1));
|
||||
mesh.setMatrixAt(index, mat);
|
||||
});
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public createGrassPatch(
|
||||
origin: Vector3,
|
||||
radius: number,
|
||||
density: number,
|
||||
pluck: number,
|
||||
getHeight: (x: number, y: number) => number,
|
||||
): Vector3[] {
|
||||
const positions: Vector3[] = [];
|
||||
const posc = new Vector2(origin.x, origin.z);
|
||||
const grassPerUnit = 1 / density;
|
||||
const actualRadius = radius / density;
|
||||
// Create patch circle
|
||||
for (let x = -actualRadius; x < actualRadius; x++) {
|
||||
for (let y = -actualRadius; y < actualRadius; y++) {
|
||||
const posi = new Vector2(origin.x + x, origin.z + y);
|
||||
if (posc.distanceTo(posi) <= actualRadius) {
|
||||
for (let u = 0; u < grassPerUnit; u++) {
|
||||
if (pluck > 0 && rand(Math.random, 0, pluck) === 0) continue;
|
||||
const pos = new Vector3(
|
||||
origin.x + x * density,
|
||||
0,
|
||||
origin.z + y * density,
|
||||
);
|
||||
pos.x += Math.random() / 2 - 1;
|
||||
pos.z += Math.random() / 2 - 1;
|
||||
pos.y = getHeight(pos.x, pos.z);
|
||||
positions.push(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return positions;
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ export function hexToString(hex: number): string {
|
||||
}
|
||||
|
||||
export function to2D(index: number, size: number): { x: number; y: number } {
|
||||
const y = index / size;
|
||||
const y = Math.floor(index / size);
|
||||
const x = index % size;
|
||||
return { x, y };
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user