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 { Socket } from 'socket.io-client';
|
||||||
import { Color } from 'three';
|
import { Color, DoubleSide, MeshBasicMaterial, Vector3 } from 'three';
|
||||||
import { isMobileOrTablet } from '../common/helper';
|
import { isMobileOrTablet } from '../common/helper';
|
||||||
import { CharacterPacket, CompositePacket } from '../common/types/packet';
|
import { CharacterPacket, CompositePacket } from '../common/types/packet';
|
||||||
import { IcyNetUser } from '../common/types/user';
|
import { IcyNetUser } from '../common/types/user';
|
||||||
@ -16,6 +16,9 @@ import { ClientWorld } from './object/world/ClientWorld';
|
|||||||
import { ClientWorldLoader } from './object/world/ClientWorldLoader';
|
import { ClientWorldLoader } from './object/world/ClientWorldLoader';
|
||||||
import { ClientWorldManifest } from './object/world/ClientWorldManifest';
|
import { ClientWorldManifest } from './object/world/ClientWorldManifest';
|
||||||
import { Renderer } from './renderer';
|
import { Renderer } from './renderer';
|
||||||
|
import { to2D } from '../common/convert';
|
||||||
|
import { Grass } from './object/model/grass';
|
||||||
|
import { BaseTexture } from './object/resource/texture';
|
||||||
|
|
||||||
export class Game {
|
export class Game {
|
||||||
public players: (Player | PlayerEntity)[] = [];
|
public players: (Player | PlayerEntity)[] = [];
|
||||||
@ -77,6 +80,71 @@ export class Game {
|
|||||||
|
|
||||||
this.renderer.scene.add(this.world.world);
|
this.renderer.scene.add(this.world.world);
|
||||||
this.renderer.scene.background = cube.texture;
|
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() {
|
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 } {
|
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;
|
const x = index % size;
|
||||||
return { x, y };
|
return { x, y };
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user