perf: huge entity gainz
This commit is contained in:
parent
b53d7e3d35
commit
a4949bd7a0
|
@ -145,7 +145,7 @@ export default class Component {
|
|||
toJSON() {
|
||||
return Component.constructor.filterDefaults(this);
|
||||
}
|
||||
update(values) {
|
||||
async update(values) {
|
||||
for (const key in values) {
|
||||
if (concrete.properties[key]) {
|
||||
this[`$$${key}`] = values[key];
|
||||
|
@ -214,10 +214,12 @@ export default class Component {
|
|||
}
|
||||
|
||||
async updateMany(entities) {
|
||||
const promises = [];
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
const [entityId, values] = entities[i];
|
||||
this.get(entityId).update(values);
|
||||
promises.push(this.get(entityId).update(values));
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,12 +14,14 @@ export default class Sprite extends Component {
|
|||
}
|
||||
set anchorX(anchorX) {
|
||||
this.$$anchor = {x: anchorX, y: this.anchorY};
|
||||
super.anchorX = anchorX;
|
||||
}
|
||||
get anchorY() {
|
||||
return this.$$anchor.y;
|
||||
}
|
||||
set anchorY(anchorY) {
|
||||
this.$$anchor = {x: this.anchorX, y: anchorY};
|
||||
super.anchorY = anchorY;
|
||||
}
|
||||
get animation() {
|
||||
return super.animation;
|
||||
|
@ -77,12 +79,14 @@ export default class Sprite extends Component {
|
|||
}
|
||||
set scaleX(scaleX) {
|
||||
this.$$scale = {x: scaleX, y: this.scaleY};
|
||||
super.scaleX = scaleX;
|
||||
}
|
||||
get scaleY() {
|
||||
return this.$$scale.y;
|
||||
}
|
||||
set scaleY(scaleY) {
|
||||
this.$$scale = {x: this.scaleX, y: scaleY};
|
||||
super.scaleY = scaleY;
|
||||
}
|
||||
get size() {
|
||||
if (!this.$$sourceJson.frames) {
|
||||
|
@ -98,6 +102,32 @@ export default class Sprite extends Component {
|
|||
const {elapsed, ...rest} = super.toNet(recipient, data);
|
||||
return rest;
|
||||
}
|
||||
update(values) {
|
||||
for (const key in values) {
|
||||
switch (key) {
|
||||
case 'anchorX': {
|
||||
this.$$anchor.x = values[key];
|
||||
break;
|
||||
}
|
||||
case 'anchorY': {
|
||||
this.$$anchor.y = values[key];
|
||||
break;
|
||||
}
|
||||
case 'scaleX': {
|
||||
this.$$scale.x = values[key];
|
||||
break;
|
||||
}
|
||||
case 'scaleY': {
|
||||
this.$$scale.y = values[key];
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.update(values);
|
||||
}
|
||||
};
|
||||
}
|
||||
async load(instance) {
|
||||
|
@ -123,6 +153,6 @@ export default class Sprite extends Component {
|
|||
scaleY: {defaultValue: 1, type: 'float32'},
|
||||
source: {type: 'string'},
|
||||
speed: {type: 'float32'},
|
||||
tint: {type: 'uint32'},
|
||||
tint: {defaultValue: 0xffffff, type: 'uint32'},
|
||||
};
|
||||
}
|
||||
|
|
18
app/react/components/pixi/aabb.js
Normal file
18
app/react/components/pixi/aabb.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {Graphics} from '@pixi/graphics';
|
||||
|
||||
export default class Aabb extends Graphics {
|
||||
constructor({color, width = 0.5}) {
|
||||
super();
|
||||
this.$$color = color;
|
||||
this.$$width = width;
|
||||
}
|
||||
redraw({x0, y0, x1, y1}) {
|
||||
this.clear();
|
||||
this.lineStyle(this.$$width, this.$$color);
|
||||
this.moveTo(x0, y0);
|
||||
this.lineTo(x1, y0);
|
||||
this.lineTo(x1, y1);
|
||||
this.lineTo(x0, y1);
|
||||
this.lineTo(x0, y0);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import {Graphics} from '@pixi/react';
|
||||
import {memo, useCallback} from 'react';
|
||||
|
||||
function Aabb({color, width = 0.5, x0, y0, x1, y1}) {
|
||||
const draw = useCallback((g) => {
|
||||
g.clear();
|
||||
g.lineStyle(width, color);
|
||||
g.moveTo(x0, y0);
|
||||
g.lineTo(x1, y0);
|
||||
g.lineTo(x1, y1);
|
||||
g.lineTo(x0, y1);
|
||||
g.lineTo(x0, y0);
|
||||
}, [color, width, x0, x1, y0, y1]);
|
||||
return (
|
||||
<Graphics draw={draw} />
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(Aabb);
|
21
app/react/components/pixi/crosshair.js
Normal file
21
app/react/components/pixi/crosshair.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import {Graphics} from '@pixi/graphics';
|
||||
|
||||
export default function crosshair() {
|
||||
const g = new Graphics();
|
||||
g.clear();
|
||||
g.lineStyle(1, 0x000000);
|
||||
g.moveTo(-5, 0);
|
||||
g.lineTo(5, 0);
|
||||
g.moveTo(0, -5);
|
||||
g.lineTo(0, 5);
|
||||
g.lineStyle(0.5, 0xffffff);
|
||||
g.moveTo(-5, 0);
|
||||
g.lineTo(5, 0);
|
||||
g.moveTo(0, -5);
|
||||
g.lineTo(0, 5);
|
||||
g.lineStyle(1, 0x000000);
|
||||
g.drawCircle(0, 0, 3);
|
||||
g.lineStyle(0.5, 0xffffff);
|
||||
g.drawCircle(0, 0, 3);
|
||||
return g;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import {Graphics} from '@pixi/react';
|
||||
import {memo, useCallback} from 'react';
|
||||
|
||||
function Crosshair() {
|
||||
const draw = useCallback((g) => {
|
||||
g.clear();
|
||||
g.lineStyle(1, 0x000000);
|
||||
g.moveTo(-5, 0);
|
||||
g.lineTo(5, 0);
|
||||
g.moveTo(0, -5);
|
||||
g.lineTo(0, 5);
|
||||
g.lineStyle(0.5, 0xffffff);
|
||||
g.moveTo(-5, 0);
|
||||
g.lineTo(5, 0);
|
||||
g.moveTo(0, -5);
|
||||
g.lineTo(0, 5);
|
||||
g.lineStyle(1, 0x000000);
|
||||
g.drawCircle(0, 0, 3);
|
||||
g.lineStyle(0.5, 0xffffff);
|
||||
g.drawCircle(0, 0, 3);
|
||||
}, []);
|
||||
return (
|
||||
<Graphics draw={draw} />
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(Crosshair);
|
|
@ -4,65 +4,85 @@ import {Container} from '@pixi/react';
|
|||
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||
|
||||
import {usePacket} from '@/react/context/client.js';
|
||||
import {useDebug} from '@/react/context/debug.js';
|
||||
import {useEcs, useEcsTick} from '@/react/context/ecs.js';
|
||||
import {useMainEntity} from '@/react/context/main-entity.js';
|
||||
import {useRadians} from '@/react/context/radians.js';
|
||||
|
||||
import Entity from './entity.jsx';
|
||||
import Entity from './entity.js';
|
||||
|
||||
export default function Entities({monopolizers, particleWorker}) {
|
||||
const [debug] = useDebug();
|
||||
const [ecs] = useEcs();
|
||||
const containerRef = useRef();
|
||||
const entities = useRef({});
|
||||
const pool = useRef([]);
|
||||
const [mainEntity] = useMainEntity();
|
||||
const radians = useRadians();
|
||||
const [willInteractWith, setWillInteractWith] = useState(0);
|
||||
const willInteractWith = useRef(0);
|
||||
const [interactionFilters] = useState([
|
||||
new AdjustmentFilter(),
|
||||
new GlowFilter({color: 0x0}),
|
||||
]);
|
||||
const [, forceRender] = useState();
|
||||
const pulse = (Math.cos(radians / 4) + 1) * 0.5;
|
||||
interactionFilters[0].brightness = (pulse * 0.75) + 1;
|
||||
interactionFilters[1].outerStrength = pulse * 0.5;
|
||||
const updateEntities = useCallback((payload) => {
|
||||
if (0 === Object.keys(entities.current).length) {
|
||||
for (let i = 0; i < 1000; ++i) {
|
||||
pool.current.push(new Entity());
|
||||
}
|
||||
}
|
||||
for (const id in payload) {
|
||||
if (!payload[id]) {
|
||||
entities.current[id].removeFromContainer();
|
||||
pool.current.push(entities.current[id]);
|
||||
delete entities.current[id];
|
||||
continue;
|
||||
}
|
||||
const entity = ecs.get(id);
|
||||
if (!entity.Position) {
|
||||
continue;
|
||||
}
|
||||
if (!entities.current[id]) {
|
||||
entities.current[id] = pool.current.length > 0 ? pool.current.pop() : new Entity();
|
||||
entities.current[id].reset(entity, debug);
|
||||
}
|
||||
if (mainEntity === id) {
|
||||
entities.current[id].setMainEntity();
|
||||
}
|
||||
entities.current[id].update(payload[id]);
|
||||
entities.current[id].addToContainer(containerRef.current);
|
||||
}
|
||||
}, [debug, ecs, mainEntity])
|
||||
useEffect(() => {
|
||||
for (const key in entities.current) {
|
||||
entities.current[key].setDebug(debug);
|
||||
}
|
||||
}, [debug]);
|
||||
usePacket('EcsChange', async () => {
|
||||
for (const id in entities.current) {
|
||||
entities.current[id].removeFromContainer();
|
||||
}
|
||||
entities.current = {};
|
||||
});
|
||||
const onEcsTickEntities = useCallback((payload) => {
|
||||
updateEntities(payload);
|
||||
}, [updateEntities]);
|
||||
useEcsTick(onEcsTickEntities);
|
||||
useEffect(() => {
|
||||
if (!ecs || !particleWorker) {
|
||||
return;
|
||||
}
|
||||
async function onMessage(diff) {
|
||||
await ecs.apply(diff.data);
|
||||
for (const id in diff.data) {
|
||||
if (!diff.data[id]) {
|
||||
delete entities.current[id]
|
||||
}
|
||||
else {
|
||||
entities.current[id] = ecs.get(id);
|
||||
}
|
||||
}
|
||||
forceRender(Math.random());
|
||||
updateEntities(diff.data);
|
||||
}
|
||||
particleWorker.addEventListener('message', onMessage);
|
||||
return () => {
|
||||
particleWorker.removeEventListener('message', onMessage);
|
||||
};
|
||||
}, [ecs, particleWorker]);
|
||||
const pulse = (Math.cos(radians / 4) + 1) * 0.5;
|
||||
interactionFilters[0].brightness = (pulse * 0.75) + 1;
|
||||
interactionFilters[1].outerStrength = pulse * 0.5;
|
||||
usePacket('EcsChange', async () => {
|
||||
entities.current = {};
|
||||
});
|
||||
const onEcsTickEntities = useCallback((payload, ecs) => {
|
||||
for (const id in payload) {
|
||||
if ('1' === id) {
|
||||
continue;
|
||||
}
|
||||
const update = payload[id];
|
||||
if (false === update) {
|
||||
delete entities.current[id];
|
||||
continue;
|
||||
}
|
||||
entities.current[id] = ecs.get(id);
|
||||
}
|
||||
forceRender(Math.random());
|
||||
}, []);
|
||||
useEcsTick(onEcsTickEntities);
|
||||
}, [ecs, particleWorker, updateEntities]);
|
||||
const onEcsTickParticles = useCallback((payload) => {
|
||||
for (const id in payload) {
|
||||
const update = payload[id];
|
||||
|
@ -77,26 +97,25 @@ export default function Entities({monopolizers, particleWorker}) {
|
|||
const onEcsTickInteractions = useCallback((payload, ecs) => {
|
||||
const main = ecs.get(mainEntity);
|
||||
if (main) {
|
||||
setWillInteractWith(main.Interacts.willInteractWith);
|
||||
if (willInteractWith.current !== main.Interacts.willInteractWith) {
|
||||
if (entities.current[willInteractWith.current]) {
|
||||
entities.current[willInteractWith.current].diffuse.filters = [];
|
||||
}
|
||||
}, [mainEntity]);
|
||||
willInteractWith.current = main.Interacts.willInteractWith;
|
||||
}
|
||||
const interacting = entities.current[main.Interacts.willInteractWith];
|
||||
if (interacting) {
|
||||
interacting.diffuse.filters = 0 === monopolizers.length
|
||||
? interactionFilters
|
||||
: [];
|
||||
}
|
||||
}
|
||||
}, [interactionFilters, mainEntity, monopolizers]);
|
||||
useEcsTick(onEcsTickInteractions);
|
||||
const renderables = [];
|
||||
for (const id in entities.current) {
|
||||
const isHighlightedInteraction = 0 === monopolizers.length && id == willInteractWith;
|
||||
renderables.push(
|
||||
<Entity
|
||||
filters={isHighlightedInteraction ? interactionFilters : null}
|
||||
entity={entities.current[id]}
|
||||
key={id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Container
|
||||
ref={containerRef}
|
||||
sortableChildren
|
||||
>
|
||||
{renderables}
|
||||
</Container>
|
||||
/>
|
||||
);
|
||||
}
|
191
app/react/components/pixi/entity.js
Normal file
191
app/react/components/pixi/entity.js
Normal file
|
@ -0,0 +1,191 @@
|
|||
import {Assets} from '@pixi/assets';
|
||||
import {Container as PixiContainer} from '@pixi/display';
|
||||
import {Sprite} from '@pixi/sprite';
|
||||
|
||||
import Aabb from './aabb.js';
|
||||
import createCrosshair from './crosshair.js';
|
||||
import {deferredLighting, PointLight} from './lights.js';
|
||||
|
||||
export default class Entity {
|
||||
static assets = {};
|
||||
attached = false;
|
||||
colliderAabb = new Aabb({color: 0xffffff});
|
||||
container = new PixiContainer();
|
||||
crosshair = createCrosshair();
|
||||
debug = new PixiContainer();
|
||||
diffuse = new Sprite();
|
||||
isMainEntity = false;
|
||||
normals = new Sprite();
|
||||
point;
|
||||
sprite = new PixiContainer();
|
||||
visibleAabb = new Aabb({color: 0xff00ff});
|
||||
constructor() {
|
||||
this.diffuse.parentGroup = deferredLighting.diffuseGroup;
|
||||
this.normals.parentGroup = deferredLighting.normalGroup;
|
||||
this.container.addChild(this.diffuse);
|
||||
this.container.addChild(this.normals);
|
||||
this.container.addChild(this.crosshair);
|
||||
this.debug.addChild(this.colliderAabb);
|
||||
this.debug.addChild(this.visibleAabb);
|
||||
}
|
||||
addToContainer(container) {
|
||||
if (this.attached || !container) {
|
||||
return;
|
||||
}
|
||||
container.addChild(this.container);
|
||||
container.addChild(this.debug);
|
||||
this.attached = true;
|
||||
}
|
||||
static async loadAsset(source = '') {
|
||||
if (!this.assets['']) {
|
||||
const {Texture} = await import('@pixi/core');
|
||||
this.assets[''] = {data: {meta: {}}, textures: {'': Texture.WHITE}};
|
||||
}
|
||||
if (!this.assets[source]) {
|
||||
this.assets[source] = Assets.load(source);
|
||||
}
|
||||
return this.assets[source];
|
||||
}
|
||||
removeFromContainer() {
|
||||
this.container.parent.removeChild(this.container);
|
||||
this.debug.parent.removeChild(this.debug);
|
||||
this.attached = false;
|
||||
}
|
||||
reset(entity, debug) {
|
||||
this.entity = entity;
|
||||
if (this.light) {
|
||||
this.container.removeChild(this.light);
|
||||
this.light = undefined;
|
||||
}
|
||||
this.setDebug(debug);
|
||||
this.isMainEntity = false;
|
||||
if (this.interactionAabb) {
|
||||
this.debug.removeChild(this.interactionAabb);
|
||||
}
|
||||
this.interactionAabb = undefined;
|
||||
}
|
||||
setDebug(isDebugging) {
|
||||
if (isDebugging) {
|
||||
this.crosshair.alpha = 1;
|
||||
this.debug.alpha = 1;
|
||||
}
|
||||
else {
|
||||
this.crosshair.alpha = 0;
|
||||
this.debug.alpha = 0;
|
||||
}
|
||||
}
|
||||
setMainEntity() {
|
||||
if (this.isMainEntity) {
|
||||
return;
|
||||
}
|
||||
this.isMainEntity = true;
|
||||
this.interactionAabb = new Aabb({color: 0x00ff00});
|
||||
this.debug.addChild(this.interactionAabb);
|
||||
}
|
||||
static textureFromAsset(asset, animation, frame) {
|
||||
if (!asset) {
|
||||
return undefined;
|
||||
}
|
||||
let texture;
|
||||
if (asset.data.animations) {
|
||||
if (!animation) {
|
||||
return undefined;
|
||||
}
|
||||
texture = asset.animations[animation][frame];
|
||||
}
|
||||
else {
|
||||
texture = asset.textures[''];
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
update({Direction, Light, Position, Sprite, VisibleAabb}) {
|
||||
if (Light) {
|
||||
if (!this.light) {
|
||||
this.light = new PointLight(
|
||||
0xffffff - 0x2244cc,
|
||||
0,
|
||||
);
|
||||
}
|
||||
this.light.brightness = Light.brightness;
|
||||
}
|
||||
if (Position) {
|
||||
const {x, y} = this.entity.Position;
|
||||
this.container.x = x;
|
||||
this.container.y = y;
|
||||
this.container.zIndex = y;
|
||||
}
|
||||
if (Sprite) {
|
||||
const {diffuse, normals} = this;
|
||||
if (!this.attached || 'alpha' in Sprite) {
|
||||
diffuse.alpha = normals.alpha = this.entity.Sprite.alpha;
|
||||
}
|
||||
if (!this.attached || 'anchorX' in Sprite) {
|
||||
diffuse.anchor.x = normals.anchor.x = this.entity.Sprite.anchorX;
|
||||
}
|
||||
if (!this.attached || 'anchorY' in Sprite) {
|
||||
diffuse.anchor.y = normals.anchor.y = this.entity.Sprite.anchorY;
|
||||
}
|
||||
if (!this.attached || 'scaleX' in Sprite) {
|
||||
diffuse.scale.x = normals.scale.x = this.entity.Sprite.scaleX;
|
||||
}
|
||||
if (!this.attached || 'scaleY' in Sprite) {
|
||||
diffuse.scale.y = normals.scale.y = this.entity.Sprite.scaleY;
|
||||
}
|
||||
if (!this.attached || 'tint' in Sprite) {
|
||||
diffuse.tint = normals.tint = this.entity.Sprite.tint;
|
||||
}
|
||||
if (
|
||||
!this.attached
|
||||
|| ('source' in Sprite)
|
||||
|| ('animation' in Sprite)
|
||||
|| ('frame' in Sprite)
|
||||
) {
|
||||
this.constructor.loadAsset(this.entity.Sprite.source).then(async (asset) => {
|
||||
const texture = await this.constructor.textureFromAsset(
|
||||
asset,
|
||||
this.entity.Sprite.animation,
|
||||
this.entity.Sprite.frame,
|
||||
);
|
||||
diffuse.texture = texture;
|
||||
if (asset.data.meta.normals) {
|
||||
const {pathname} = new URL(
|
||||
Sprite.source
|
||||
.split('/')
|
||||
.slice(0, -1)
|
||||
.concat(asset.data.meta.normals)
|
||||
.join('/'),
|
||||
'http://example.org',
|
||||
);
|
||||
this.constructor.loadAsset(pathname).then(async (asset) => {
|
||||
const texture = await this.constructor.textureFromAsset(
|
||||
asset,
|
||||
this.entity.Sprite.animation,
|
||||
this.entity.Sprite.frame,
|
||||
);
|
||||
normals.texture = texture;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Direction) {
|
||||
const {diffuse, normals} = this;
|
||||
if ('direction' in Direction) {
|
||||
if (this.entity.Sprite.rotates) {
|
||||
const rotation = Direction.direction + this.entity.Sprite.rotation;
|
||||
diffuse.rotation = rotation;
|
||||
normals.rotation = rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.entity.Collider) {
|
||||
this.colliderAabb.redraw(this.entity.Collider.aabb);
|
||||
}
|
||||
if (VisibleAabb) {
|
||||
this.visibleAabb.redraw(this.entity.VisibleAabb);
|
||||
}
|
||||
if (this.isMainEntity) {
|
||||
this.interactionAabb.redraw(this.entity.Interacts.aabb());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
import {Container} from '@pixi/react';
|
||||
import {memo} from 'react';
|
||||
|
||||
import {useDebug} from '@/react/context/debug.js';
|
||||
import {useMainEntity} from '@/react/context/main-entity.js';
|
||||
|
||||
import Aabb from './aabb.jsx';
|
||||
import Crosshair from './crosshair.jsx';
|
||||
import Light from './light.jsx';
|
||||
import SpriteComponent from './sprite.jsx';
|
||||
|
||||
function Entity({entity, ...rest}) {
|
||||
const [debug] = useDebug();
|
||||
const [mainEntity] = useMainEntity();
|
||||
if (!entity) {
|
||||
return false;
|
||||
}
|
||||
const {Direction, id, Sprite} = entity;
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
x={entity.Position.x}
|
||||
y={entity.Position.y}
|
||||
zIndex={entity.Position?.y || 0}
|
||||
>
|
||||
{entity.Sprite && (
|
||||
<SpriteComponent
|
||||
alpha={Sprite.alpha}
|
||||
anchor={Sprite.anchor}
|
||||
animation={Sprite.animation}
|
||||
direction={Direction?.direction}
|
||||
frame={Sprite.frame}
|
||||
id={id}
|
||||
scale={Sprite.scale}
|
||||
rotates={Sprite.rotates}
|
||||
rotation={Sprite.rotation}
|
||||
source={Sprite.source}
|
||||
tint={Sprite.tint}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
{entity.Light && (
|
||||
<Light
|
||||
brightness={entity.Light.brightness}
|
||||
/>
|
||||
)}
|
||||
{debug && entity.Position && (
|
||||
<Crosshair />
|
||||
)}
|
||||
</Container>
|
||||
{debug && (
|
||||
<Aabb
|
||||
color={0xff00ff}
|
||||
{...entity.VisibleAabb}
|
||||
/>
|
||||
)}
|
||||
{debug && entity.Collider && (
|
||||
<>
|
||||
<Aabb
|
||||
color={0xffffff}
|
||||
width={0.5}
|
||||
{...entity.Collider.aabb}
|
||||
/>
|
||||
{entity.Collider.aabbs.map((aabb, i) => (
|
||||
<Aabb
|
||||
color={0xffff00}
|
||||
width={0.25}
|
||||
key={i}
|
||||
{...aabb}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{debug && mainEntity == entity.id && (
|
||||
<Aabb
|
||||
color={0x00ff00}
|
||||
{...entity.Interacts.aabb()}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(Entity);
|
|
@ -1,34 +0,0 @@
|
|||
import {PixiComponent} from '@pixi/react';
|
||||
|
||||
import {PointLight} from './lights.js';
|
||||
|
||||
const LightInternal = PixiComponent('Light', {
|
||||
create({brightness}) {
|
||||
const light = new PointLight(
|
||||
0xffffff - 0x2244cc,
|
||||
brightness,
|
||||
);
|
||||
// light.shader.program.fragmentSrc = light.shader.program.fragmentSrc.replace(
|
||||
// 'float D = length(lightVector)',
|
||||
// 'float D = length(lightVector) / 1.0',
|
||||
// );
|
||||
// light.shader.program.fragmentSrc = light.shader.program.fragmentSrc.replace(
|
||||
// 'intensity = diffuse * attenuation',
|
||||
// 'intensity = diffuse * (attenuation * 2.0)',
|
||||
// );
|
||||
// light.falloff = [0.5, 5, 50];
|
||||
// light.falloff = light.falloff.map((n, i) => n / (2 + i));
|
||||
// light.parentGroup = deferredLighting.lightGroup;
|
||||
// delete light.parentGroup;
|
||||
return light;
|
||||
},
|
||||
});
|
||||
|
||||
export default function Light({brightness}) {
|
||||
return (
|
||||
<LightInternal
|
||||
brightness={brightness}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
import {Sprite as PixiSprite} from '@pixi/react';
|
||||
import {memo, useEffect, useState} from 'react';
|
||||
|
||||
import {useAsset} from '@/react/context/assets.js';
|
||||
|
||||
import {deferredLighting} from './lights.js';
|
||||
|
||||
function textureFromAsset(asset, animation, frame) {
|
||||
if (!asset) {
|
||||
return undefined;
|
||||
}
|
||||
let texture;
|
||||
if (asset.data.animations) {
|
||||
if (!animation) {
|
||||
return undefined;
|
||||
}
|
||||
texture = asset.animations[animation][frame];
|
||||
}
|
||||
else {
|
||||
texture = asset.textures[''];
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
function Sprite(props) {
|
||||
const {
|
||||
alpha,
|
||||
anchor,
|
||||
animation,
|
||||
direction,
|
||||
frame,
|
||||
scale,
|
||||
rotates,
|
||||
rotation,
|
||||
source,
|
||||
tint,
|
||||
...rest
|
||||
} = props;
|
||||
const [mounted, setMounted] = useState();
|
||||
const [normals, setNormals] = useState();
|
||||
const [normalsMounted, setNormalsMounted] = useState();
|
||||
const asset = useAsset(source);
|
||||
const normalsAsset = useAsset(normals);
|
||||
useEffect(() => {
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
const {normals} = asset.data.meta;
|
||||
if (normals) {
|
||||
const {pathname} = new URL(
|
||||
source.split('/').slice(0, -1).concat(normals).join('/'),
|
||||
'http://example.org',
|
||||
);
|
||||
setNormals(pathname);
|
||||
}
|
||||
}, [asset, source]);
|
||||
const texture = textureFromAsset(
|
||||
asset,
|
||||
animation,
|
||||
frame,
|
||||
);
|
||||
const normalsTexture = textureFromAsset(
|
||||
normalsAsset,
|
||||
animation,
|
||||
frame,
|
||||
);
|
||||
if (mounted) {
|
||||
mounted.parentGroup = deferredLighting.diffuseGroup;
|
||||
}
|
||||
if (normalsMounted) {
|
||||
normalsMounted.parentGroup = deferredLighting.normalGroup;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{texture && (
|
||||
<PixiSprite
|
||||
alpha={alpha}
|
||||
anchor={anchor}
|
||||
ref={setMounted}
|
||||
{...(rotates ? {rotation: direction + rotation} : {})}
|
||||
scale={scale}
|
||||
texture={texture}
|
||||
{...(0 !== tint ? {tint} : {})}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
{normalsTexture && (
|
||||
<PixiSprite
|
||||
alpha={alpha}
|
||||
anchor={anchor}
|
||||
ref={setNormalsMounted}
|
||||
{...(rotates ? {rotation: direction + rotation} : {})}
|
||||
scale={scale}
|
||||
texture={normalsTexture}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(Sprite);
|
|
@ -10,13 +10,11 @@ const TargetingGhostInternal = PixiComponent('TargetingGhost', {
|
|||
create: () => {
|
||||
// Solid target square.
|
||||
const target = new Graphics();
|
||||
target.alpha = 0.7;
|
||||
target.lineStyle(1, 0xffffff);
|
||||
target.drawRect(0.5, 0.5, tileSize.x, tileSize.y);
|
||||
target.pivot = {x: tileSize.x / 2, y: tileSize.y / 2};
|
||||
// Inner spinny part.
|
||||
const targetInner = new Graphics();
|
||||
targetInner.alpha = 0.3;
|
||||
targetInner.lineStyle(3, 0x333333);
|
||||
targetInner.beginFill(0xdddddd);
|
||||
targetInner.pivot = {x: tileSize.x / 2, y: tileSize.y / 2};
|
||||
|
@ -25,6 +23,7 @@ const TargetingGhostInternal = PixiComponent('TargetingGhost', {
|
|||
targetInner.position.y = 0.5;
|
||||
// ...
|
||||
const container = new Container();
|
||||
container.alpha = 0.4;
|
||||
container.addChild(target, targetInner);
|
||||
return container;
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue
Block a user