Files
red-valley/resources/[framework]/[depends]/phone-render/script.js
2026-03-29 21:41:17 +03:00

240 lines
7.7 KiB
JavaScript

// UTK RENDER https://github.com/utkuali/utk_render
import { CfxTexture, LinearFilter, Mesh, NearestFilter, OrthographicCamera, PlaneBufferGeometry, RGBAFormat, Scene, ShaderMaterial, UnsignedByteType, WebGLRenderTarget, WebGLRenderer } from "/module/Three.js";
var isAnimated = false;
var MainRender;
// from https://stackoverflow.com/a/12300351
function dataURItoBlob(dataURI) {
const byteString = atob(dataURI.split(',')[1]);
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ab], { type: mimeString });
return blob;
}
const normalModeAspectRatio = 1.6;
// citizenfx/screenshot-basic
class GameRender {
constructor() {
window.addEventListener('resize', this.resize);
this.width = window.innerWidth;
this.height = window.innerHeight;
this.landscape = false;
$('#camera-canvas').css({
width: '100%',
transform: 'rotate(0deg)'
});
this.resizedWidth = this.width / normalModeAspectRatio;
this.resizedHeight = this.height;
const cameraRTT = new OrthographicCamera(this.width / -2, this.width / 2, this.height / 2, this.height / -2, -10000, 10000);
cameraRTT.position.z = 100;
cameraRTT.setViewOffset(this.width, this.height, 0, 0, this.width, this.height);
const sceneRTT = new Scene();
const rtTexture = new WebGLRenderTarget(this.width, this.height, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat, type: UnsignedByteType });
const gameTexture = new CfxTexture();
gameTexture.needsUpdate = true;
const material = new ShaderMaterial({
uniforms: { "tDiffuse": { value: gameTexture } },
vertexShader: `
varying vec2 vUv;
void main() {
vUv = vec2(uv.x, 1.0-uv.y);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
uniform sampler2D tDiffuse;
void main() {
gl_FragColor = texture2D(tDiffuse, vUv);
}
`
});
this.material = material;
const plane = new PlaneBufferGeometry(this.width, this.height);
const quad = new Mesh(plane, material);
quad.position.z = -100;
sceneRTT.add(quad);
const renderer = new WebGLRenderer();
renderer.setSize(this.width, this.height);
renderer.autoClear = false;
let appendArea = document.createElement("div");
appendArea.id = "three-game-render";
document.body.append(appendArea);
appendArea.appendChild(renderer.domElement);
appendArea.style.display = 'none';
this.renderer = renderer;
this.rtTexture = rtTexture;
this.sceneRTT = sceneRTT;
this.cameraRTT = cameraRTT;
this.gameTexture = gameTexture;
this.animate = this.animate.bind(this);
this.animate();
}
setLandscape(bool) {
this.landscape = bool;
if (bool) {
this.resizedWidth = this.width;
this.resizedHeight = this.height;
$('#camera-canvas').css({
width: '155%',
transform: 'rotate(90deg)'
});
Rotate(true)
} else {
this.resizedWidth = this.width / normalModeAspectRatio;
this.resizedHeight = this.height;
$('#camera-canvas').css({
width: '100%',
transform: 'rotate(0deg)'
});
Rotate(false)
}
this.resize()
}
getLandscape() {
return this.landscape;
}
resize(screenshot) {
const cameraRTT = new OrthographicCamera(this.width / -2, this.width / 2, this.height / 2, this.height / -2, -10000, 10000);
if (screenshot === true) {
// cameraRTT.setViewOffset(this.width, this.height, 0, 0, this.width, this.height);
} else {
// const width = Math.floor(this.height / 2);
// const width = Math.floor(this.height * 10 / 23);
// cameraRTT.setViewOffset(this.width, this.height, 0, 0, this.width, this.height);
const portrait = this.width === this.resizedWidth
const width = portrait ? this.resizedWidth : this.resizedWidth / 3.5;
// cameraRTT.setViewOffset(this.width, this.height, width, 0, this.resizedWidth, this.height);
if (this.canvas) this.canvas.width = this.resizedWidth;
// cameraRTT.setViewOffset(this.width, this.height, this.width, 0, width, this.height);
}
this.cameraRTT = cameraRTT;
const sceneRTT = new Scene();
const plane = new PlaneBufferGeometry(this.width, this.height);
const quad = new Mesh(plane, this.material);
quad.position.z = -100;
sceneRTT.add(quad);
this.sceneRTT = sceneRTT;
this.rtTexture = new WebGLRenderTarget(this.width, this.height, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat, type: UnsignedByteType });
this?.renderer?.setSize?.(this.width, this.height);
}
animate() {
if (isAnimated) {
this.renderer.clear();
this.renderer.render(this.sceneRTT, this.cameraRTT, this.rtTexture, true);
const width = this.width;
const height = this.height;
if (this.canvas.width !== this.resizedWidth || this.canvas.height !== height) {
this.canvas.width = this.resizedWidth;
this.canvas.height = height;
}
const read = new Uint8Array(width * height * 4);
this.renderer.readRenderTargetPixels(this.rtTexture, 0, 0, width, height, read);
const cxt = this.canvas.getContext('2d');
const imageData = new ImageData(new Uint8ClampedArray(read.buffer), width, height);
cxt.putImageData(imageData, 0, 0);
requestAnimationFrame(this.animate);
}
}
createTempCanvas() {
this.canvas = document.createElement("canvas");
this.canvas.style.display = 'inline';
this.canvas.width = this.width;
this.canvas.height = this.height;
}
renderToTarget(element) {
this.resize(false);
this.canvas = element;
isAnimated = true;
this.animate()
}
requestScreenshot = (url, field) => new Promise(res => {
if (!url || !field) return console.error('url or field is not defined');
isAnimated = true;
const imageURL = this.canvas.toDataURL("image/png", 1.0);
const formData = new FormData();
formData.append(field, dataURItoBlob(imageURL), `screenshot.png`);
fetch(url, {
method: 'POST',
mode: 'cors',
body: formData
})
.then(response => response.text())
.then(text => {
text = JSON.parse(text);
if (!text.attachments) return res(text.data.url);
res(text.attachments[0].proxy_url)
})
.catch(err => {
console.error(err);
res(false);
});
})
stop() {
isAnimated = false;
if (this.canvas) {
if (this.canvas.style.display != "none") {
this.canvas.style.display = "none";
}
}
this.resize(true);
this.resizedWidth = this.width;
this.resizedHeight = this.height;
this.setLandscape(false);
}
}
setTimeout(() => {
MainRender = new GameRender();
window.MainRender = MainRender;
}, 1000);