Skip to content

Instantly share code, notes, and snippets.

@duydang2311
Last active October 13, 2022 14:13
Show Gist options
  • Save duydang2311/992d5cb5f9a6267cf39cc84e8636dbbb to your computer and use it in GitHub Desktop.
Save duydang2311/992d5cb5f9a6267cf39cc84e8636dbbb to your computer and use it in GitHub Desktop.
[alt:V] RmlUi document class for drawing 3D texts
import * as alt from 'alt-client';
export interface WorldElementData {
pos: () => alt.Vector3;
text: () => string;
losCheck?: () => boolean;
rangeSquared?: number;
}
export interface WorldDocumentConfig {
rmlPath: string;
renderInterval: () => number;
}
export interface IWorldDocument {
add: (data: WorldElementData) => alt.RmlElement;
remove: (element: alt.RmlElement) => boolean;
}
export class WorldDocument implements IWorldDocument {
private readonly dataMap = new Map<alt.RmlElement, WorldElementData>();
private readonly document: alt.RmlDocument;
private renderInterval: number = 0;
private now: number = 0;
constructor(config: WorldDocumentConfig) {
this.document = new alt.RmlDocument(config.rmlPath);
this.document.show(false, false);
this.document.body.style['font-family'] = '<your font>'; // to fix font not loaded issue with Rml although font-family is specified in rcss
this.renderInterval = config.renderInterval();
alt.setInterval(() => {
this.renderInterval = config.renderInterval();
}, 250);
alt.everyTick(this.render);
}
private readonly render = () => {
const now = Date.now();
if (now - this.now < this.renderInterval) {
return;
}
this.now = now;
const local = alt.Player.local;
const localPos = local.pos;
for (const [element, data] of this.dataMap) {
const pos = data.pos();
const dist = !data.rangeSquared
? 1
: this.clampIfLower(
0,
Math.round((1 - this.distanceSquared(localPos, pos) / data.rangeSquared) * 1000) / 1000);
if (
!alt.isPointOnScreen(pos.x, pos.y, pos.z) ||
(data.losCheck && !data.losCheck())
) {
element.style['opacity'] = '0';
continue;
}
const screen = alt.worldToScreen(pos.x, pos.y, pos.z);
element.style['top'] = `${Math.round(screen.y * 100) / 100}px`;
element.style['left'] = `${Math.round(screen.x * 100) / 100}px`;
element.style['opacity'] = '' + dist;
element.style['font-size'] = `${dist}em'; // or `${0.5 + dist * 0.5}em` or for slighter scale
element.innerRML = data.text();
}
};
public readonly add = (data: WorldElementData): alt.RmlElement => {
const element = this.document.createElement('p');
element.style['position'] = 'absolute';
element.style['white-space'] = 'nowrap';
element.innerRML = data.text();
this.document.body.appendChild(element);
this.dataMap.set(element, data);
return element;
};
public readonly remove = (element: alt.RmlElement) => {
this.document.body.removeChild(element);
return this.dataMap.delete(element);
};
private readonly clampIfLower = (min: number, value: number) => {
return value < min ? min : value;
};
private readonly distanceSquared = (v1: alt.Vector3, v2: alt.Vector3) => {
const dx = v1.x - v2.x;
const dy = v1.y - v2.y;
const dz = v1.z - v2.z;
return dx * dx + dy * dy + dz * dz;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment