This commit is contained in:
Evert Prants 2022-04-14 20:37:56 +03:00
parent 4aa2dd0237
commit dd9c35d3bd
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
7 changed files with 167 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -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() {

View 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;
}
}

View File

@ -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 };
} }