зеркало из https://github.com/microsoft/scenepic.git
Cleanup for v1.1.0 (#58)
In prep for the 1.1 minor release a few final issues were addressed: New Features - Full pinch, zoom and rotate with two fingers is now supported Bug fixes - Dropdown menus will now correctly appear on top of canvases in the z-order - Dropdown menus will align on the opposite side if not doing so would result appearing outside the viewbox
This commit is contained in:
Родитель
d9b4726a1a
Коммит
892b2c4996
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,5 +1,16 @@
|
|||
# Changelog
|
||||
|
||||
## [2023-03-13 - Version 1.1.0](https://github.com/microsoft/scenepic/releases/tag/v1.1.0)
|
||||
As part of this 1.1 minor release a few final issues were addressed:
|
||||
|
||||
New Features
|
||||
- Full pinch, zoom and rotate with two fingers is now supported
|
||||
|
||||
Bug fixes
|
||||
- Dropdown menus will now correctly appear on top of canvases in the z-order
|
||||
- Dropdown menus will align on the opposite side if not doing so would result
|
||||
appearing outside the viewbox
|
||||
|
||||
## [2023-03-13 - Version 1.0.19](https://github.com/microsoft/scenepic/releases/tag/v1.0.19)
|
||||
Point release adding pan/zoom functionality to 2D canvases.
|
||||
|
||||
|
|
|
@ -1 +1,9 @@
|
|||
Point release adding pan/zoom functionality to 2D canvases.
|
||||
As part of this 1.1 minor release a few final issues were addressed:
|
||||
|
||||
New Features
|
||||
- Full pinch, zoom and rotate with two fingers is now supported
|
||||
|
||||
Bug fixes
|
||||
- Dropdown menus will now correctly appear on top of canvases in the z-order
|
||||
- Dropdown menus will align on the opposite side if not doing so would result
|
||||
appearing outside the viewbox
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.0.19
|
||||
1.1.0
|
|
@ -22,7 +22,7 @@ copyright = "2021, Microsoft"
|
|||
author = "ScenePic Team"
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = "1.0.19"
|
||||
release = "1.1.0"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "scenepic",
|
||||
"version": "1.0.19",
|
||||
"version": "1.1.0",
|
||||
"description": "3D Visualization Made Easy",
|
||||
"author": "ScenePic Team",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
"""Test script for dropdown CSS"""
|
||||
|
||||
import random
|
||||
from typing import List
|
||||
import numpy as np
|
||||
import scenepic as sp
|
||||
|
||||
|
||||
def _create_cubes(scene: sp.Scene, num_cubes: int) -> List[sp.Mesh]:
|
||||
meshes = []
|
||||
for i in range(num_cubes):
|
||||
color = np.random.rand(3)
|
||||
mesh: sp.Mesh = scene.create_mesh(layer_id="cube{}".format(i), shared_color=color)
|
||||
mesh.add_cube()
|
||||
meshes.append(mesh)
|
||||
|
||||
return meshes
|
||||
|
||||
|
||||
def _add_objects(canvas: sp.Canvas3D, cubes: List[sp.Mesh]):
|
||||
frame = canvas.create_frame()
|
||||
num_cubes = np.random.randint(5, 10)
|
||||
choices = np.random.choice(len(cubes), num_cubes, replace=False)
|
||||
for index in choices:
|
||||
pos = np.random.rand(3) * 2 - 1
|
||||
frame.add_mesh(cubes[index], sp.Transforms.Translate(pos))
|
||||
|
||||
|
||||
def _add_circles(canvas: sp.Canvas2D):
|
||||
frame: sp.Frame2D = canvas.create_frame()
|
||||
num_circles = np.random.randint(5, 10)
|
||||
for i in range(num_circles):
|
||||
pos = np.random.rand(2) * 100
|
||||
radius = np.random.random() * 10
|
||||
color = np.random.rand(3)
|
||||
frame.add_circle(pos[0], pos[1], radius, fill_color=color, layer_id="circle{}".format(i))
|
||||
|
||||
|
||||
def _main():
|
||||
scene = sp.Scene()
|
||||
|
||||
cubes = _create_cubes(scene, 20)
|
||||
for row in range(1, 5):
|
||||
for col in range(1, 5):
|
||||
canvasID = "canvas" + str(row) + str(col)
|
||||
if random.random() < 0.5:
|
||||
canvas = scene.create_canvas_3d(canvasID, 100, 100)
|
||||
_add_objects(canvas, cubes)
|
||||
else:
|
||||
canvas = scene.create_canvas_2d(canvasID, 100, 100)
|
||||
_add_circles(canvas)
|
||||
|
||||
scene.place(canvasID, str(row), str(col))
|
||||
|
||||
scene.grid("400px", "auto auto auto auto", "auto auto auto auto")
|
||||
scene.save_as_html("overlap.html")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_main()
|
|
@ -3,4 +3,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
__version__ = "1.0.19"
|
||||
__version__ = "1.1.0"
|
||||
|
|
|
@ -79,6 +79,7 @@ var css = `
|
|||
top: 5px;
|
||||
right: 5px;
|
||||
user-select: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scenepic-dropdown:after {
|
||||
|
@ -99,6 +100,14 @@ var css = `
|
|||
margin-top: -9px;
|
||||
}
|
||||
|
||||
.scenepic-left-table {
|
||||
left: 1px
|
||||
}
|
||||
|
||||
.scenepic-right-table {
|
||||
right: -1px
|
||||
}
|
||||
|
||||
.scenepic-dropdown-table {
|
||||
list-style: none !important;
|
||||
margin: 0 !important;
|
||||
|
@ -106,7 +115,6 @@ var css = `
|
|||
position: absolute;
|
||||
top: 100%;
|
||||
border: inherit;
|
||||
right: -1px;
|
||||
visibility: hidden;
|
||||
background-color: white;
|
||||
pointer-events: none;
|
||||
|
@ -330,15 +338,14 @@ input.scenepic-volume-slider {
|
|||
|
||||
var initialized = false;
|
||||
|
||||
export default function InitializeCSSStyles()
|
||||
{
|
||||
if (initialized) return;
|
||||
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
var style = document.createElement("style");
|
||||
style.type = "text/css";
|
||||
style.appendChild(document.createTextNode(css));
|
||||
head.appendChild(style);
|
||||
export default function InitializeCSSStyles() {
|
||||
if (initialized) return;
|
||||
|
||||
initialized = true;
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
var style = document.createElement("style");
|
||||
style.type = "text/css";
|
||||
style.appendChild(document.createTextNode(css));
|
||||
head.appendChild(style);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
|
|
@ -1,30 +1,26 @@
|
|||
import Misc from "./Misc"
|
||||
import {ObjectCache, CanvasBase} from "./CanvasBase"
|
||||
import { ObjectCache, CanvasBase } from "./CanvasBase"
|
||||
import { mat3, vec2 } from "gl-matrix";
|
||||
|
||||
abstract class Primitive
|
||||
{
|
||||
readonly layerId : string;
|
||||
abstract class Primitive {
|
||||
readonly layerId: string;
|
||||
|
||||
constructor(layerId: string)
|
||||
{
|
||||
constructor(layerId: string) {
|
||||
this.layerId = layerId;
|
||||
}
|
||||
|
||||
GetObjectCacheId() : string { return null; }
|
||||
GetObjectCacheId(): string { return null; }
|
||||
abstract Draw(HTMLCanvasElement, CanvasRenderingContext2D, ObjectCache);
|
||||
}
|
||||
|
||||
class LinesPrimitive extends Primitive
|
||||
{
|
||||
readonly strokeStyle : string;
|
||||
readonly lineWidth : number;
|
||||
readonly fillStyle : string;
|
||||
readonly coordinates : Float32Array;
|
||||
readonly closePath : boolean;
|
||||
class LinesPrimitive extends Primitive {
|
||||
readonly strokeStyle: string;
|
||||
readonly lineWidth: number;
|
||||
readonly fillStyle: string;
|
||||
readonly coordinates: Float32Array;
|
||||
readonly closePath: boolean;
|
||||
|
||||
constructor(strokeStyle : string, lineWidth : number, fillStyle : string, coordinates : Float32Array, closePath : boolean, layerId : string)
|
||||
{
|
||||
constructor(strokeStyle: string, lineWidth: number, fillStyle: string, coordinates: Float32Array, closePath: boolean, layerId: string) {
|
||||
super(layerId);
|
||||
this.strokeStyle = strokeStyle;
|
||||
this.lineWidth = lineWidth;
|
||||
|
@ -32,22 +28,19 @@ class LinesPrimitive extends Primitive
|
|||
this.coordinates = coordinates;
|
||||
this.closePath = closePath;
|
||||
}
|
||||
|
||||
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
|
||||
{
|
||||
|
||||
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
|
||||
context.beginPath();
|
||||
context.moveTo(this.coordinates[0] * window.devicePixelRatio, this.coordinates[1] * window.devicePixelRatio);
|
||||
for(var i = 0; i < this.coordinates.length; i += 2)
|
||||
for (var i = 0; i < this.coordinates.length; i += 2)
|
||||
context.lineTo(this.coordinates[i] * window.devicePixelRatio, this.coordinates[i + 1] * window.devicePixelRatio);
|
||||
if (this.closePath)
|
||||
context.closePath();
|
||||
if (this.fillStyle != null)
|
||||
{
|
||||
if (this.fillStyle != null) {
|
||||
context.fillStyle = this.fillStyle;
|
||||
context.fill();
|
||||
}
|
||||
if (this.strokeStyle != null && this.lineWidth > 0)
|
||||
{
|
||||
if (this.strokeStyle != null && this.lineWidth > 0) {
|
||||
context.strokeStyle = this.strokeStyle;
|
||||
context.lineWidth = this.lineWidth;
|
||||
context.stroke();
|
||||
|
@ -55,16 +48,14 @@ class LinesPrimitive extends Primitive
|
|||
}
|
||||
}
|
||||
|
||||
class CirclePrimitive extends Primitive
|
||||
{
|
||||
readonly strokeStyle : string;
|
||||
readonly lineWidth : number;
|
||||
readonly fillStyle : string;
|
||||
readonly center : Float32Array;
|
||||
readonly radius : number;
|
||||
class CirclePrimitive extends Primitive {
|
||||
readonly strokeStyle: string;
|
||||
readonly lineWidth: number;
|
||||
readonly fillStyle: string;
|
||||
readonly center: Float32Array;
|
||||
readonly radius: number;
|
||||
|
||||
constructor(strokeStyle : string, lineWidth : number, fillStyle : string, center : Float32Array, radius : number, layerId : string)
|
||||
{
|
||||
constructor(strokeStyle: string, lineWidth: number, fillStyle: string, center: Float32Array, radius: number, layerId: string) {
|
||||
super(layerId);
|
||||
this.strokeStyle = strokeStyle;
|
||||
this.lineWidth = lineWidth;
|
||||
|
@ -72,18 +63,15 @@ class CirclePrimitive extends Primitive
|
|||
this.center = center;
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
|
||||
{
|
||||
|
||||
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
|
||||
context.beginPath();
|
||||
context.arc(this.center[0] * window.devicePixelRatio, this.center[1] * window.devicePixelRatio, this.radius * window.devicePixelRatio, 0.0, 2.0 * Math.PI, false);
|
||||
if (this.fillStyle != null)
|
||||
{
|
||||
if (this.fillStyle != null) {
|
||||
context.fillStyle = this.fillStyle;
|
||||
context.fill();
|
||||
}
|
||||
if (this.strokeStyle != null && this.lineWidth > 0)
|
||||
{
|
||||
if (this.strokeStyle != null && this.lineWidth > 0) {
|
||||
context.strokeStyle = this.strokeStyle;
|
||||
context.lineWidth = this.lineWidth;
|
||||
context.stroke();
|
||||
|
@ -91,16 +79,14 @@ class CirclePrimitive extends Primitive
|
|||
}
|
||||
}
|
||||
|
||||
class TextPrimitive extends Primitive
|
||||
{
|
||||
readonly text : string;
|
||||
readonly fillStyle : string;
|
||||
readonly sizeInPixels : number;
|
||||
readonly fontFamily : string;
|
||||
readonly position : Float32Array;
|
||||
class TextPrimitive extends Primitive {
|
||||
readonly text: string;
|
||||
readonly fillStyle: string;
|
||||
readonly sizeInPixels: number;
|
||||
readonly fontFamily: string;
|
||||
readonly position: Float32Array;
|
||||
|
||||
constructor(text : string, fillStyle : string, sizeInPixels : number, fontFamily : string, position : Float32Array, layerId : string)
|
||||
{
|
||||
constructor(text: string, fillStyle: string, sizeInPixels: number, fontFamily: string, position: Float32Array, layerId: string) {
|
||||
super(layerId);
|
||||
this.text = text;
|
||||
this.fillStyle = fillStyle;
|
||||
|
@ -109,8 +95,7 @@ class TextPrimitive extends Primitive
|
|||
this.position = position;
|
||||
}
|
||||
|
||||
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
|
||||
{
|
||||
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
|
||||
context.fillStyle = this.fillStyle;
|
||||
let sizeInPixels = this.sizeInPixels * window.devicePixelRatio;
|
||||
let left = this.position[0] * window.devicePixelRatio;
|
||||
|
@ -121,16 +106,14 @@ class TextPrimitive extends Primitive
|
|||
}
|
||||
}
|
||||
|
||||
class ImagePrimitive extends Primitive
|
||||
{
|
||||
readonly imageId : string;
|
||||
readonly positionType : string;
|
||||
readonly position : Float32Array
|
||||
readonly scale : number;
|
||||
readonly smoothed : boolean;
|
||||
class ImagePrimitive extends Primitive {
|
||||
readonly imageId: string;
|
||||
readonly positionType: string;
|
||||
readonly position: Float32Array
|
||||
readonly scale: number;
|
||||
readonly smoothed: boolean;
|
||||
|
||||
constructor(imageId : string, positionType : string, position : Float32Array, scale : number, smoothed : boolean, layerId : string)
|
||||
{
|
||||
constructor(imageId: string, positionType: string, position: Float32Array, scale: number, smoothed: boolean, layerId: string) {
|
||||
super(layerId);
|
||||
this.imageId = imageId;
|
||||
this.positionType = positionType;
|
||||
|
@ -139,13 +122,11 @@ class ImagePrimitive extends Primitive
|
|||
this.smoothed = smoothed;
|
||||
}
|
||||
|
||||
GetObjectCacheId()
|
||||
{
|
||||
GetObjectCacheId() {
|
||||
return this.imageId;
|
||||
}
|
||||
|
||||
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
|
||||
{
|
||||
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
|
||||
// Get current image
|
||||
var currentImage = objectCache.GetObject(this.imageId);
|
||||
if (currentImage == null) return; // Indicates image not available (e.g. not completely loaded yet)
|
||||
|
@ -157,8 +138,7 @@ class ImagePrimitive extends Primitive
|
|||
var centered = true;
|
||||
var scaleX = canvas.clientWidth / currentImage.naturalWidth;
|
||||
var scaleY = canvas.clientHeight / currentImage.naturalHeight;
|
||||
switch(this.positionType)
|
||||
{
|
||||
switch (this.positionType) {
|
||||
case "fill":
|
||||
scaleX = scaleY = Math.max(scaleX, scaleY);
|
||||
break;
|
||||
|
@ -184,16 +164,14 @@ class ImagePrimitive extends Primitive
|
|||
}
|
||||
}
|
||||
|
||||
class VideoPrimitive extends Primitive
|
||||
{
|
||||
readonly videoId : string;
|
||||
readonly positionType : string;
|
||||
readonly position : Float32Array
|
||||
readonly scale : number;
|
||||
readonly smoothed : boolean;
|
||||
class VideoPrimitive extends Primitive {
|
||||
readonly videoId: string;
|
||||
readonly positionType: string;
|
||||
readonly position: Float32Array
|
||||
readonly scale: number;
|
||||
readonly smoothed: boolean;
|
||||
|
||||
constructor(videoId : string, positionType : string, position : Float32Array, scale : number, smoothed : boolean, layerId : string)
|
||||
{
|
||||
constructor(videoId: string, positionType: string, position: Float32Array, scale: number, smoothed: boolean, layerId: string) {
|
||||
super(layerId);
|
||||
this.videoId = videoId;
|
||||
this.positionType = positionType;
|
||||
|
@ -202,15 +180,13 @@ class VideoPrimitive extends Primitive
|
|||
this.smoothed = smoothed;
|
||||
}
|
||||
|
||||
GetObjectCacheId()
|
||||
{
|
||||
GetObjectCacheId() {
|
||||
return this.videoId;
|
||||
}
|
||||
|
||||
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
|
||||
{
|
||||
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
|
||||
// Get current image
|
||||
let currentVideo : HTMLVideoElement = objectCache.GetObject(this.videoId);
|
||||
let currentVideo: HTMLVideoElement = objectCache.GetObject(this.videoId);
|
||||
if (currentVideo == null) return; // Indicates video not available (e.g. not completely loaded yet)
|
||||
|
||||
// Turn off image smoothing
|
||||
|
@ -220,8 +196,7 @@ class VideoPrimitive extends Primitive
|
|||
let centered = true;
|
||||
let scaleX = canvas.clientWidth / currentVideo.videoWidth;
|
||||
let scaleY = canvas.clientHeight / currentVideo.videoHeight;
|
||||
switch(this.positionType)
|
||||
{
|
||||
switch (this.positionType) {
|
||||
case "fill":
|
||||
scaleX = scaleY = Math.max(scaleX, scaleY);
|
||||
break;
|
||||
|
@ -248,30 +223,31 @@ class VideoPrimitive extends Primitive
|
|||
}
|
||||
|
||||
|
||||
export default class Canvas2D extends CanvasBase
|
||||
{
|
||||
context : CanvasRenderingContext2D = null; // Rendering context
|
||||
export default class Canvas2D extends CanvasBase {
|
||||
context: CanvasRenderingContext2D = null; // Rendering context
|
||||
|
||||
backgroundStyle : string; // Background color
|
||||
backgroundStyle: string; // Background color
|
||||
|
||||
// Dictionary from primitiveId to layer settings (wireframe, filled, opacity)
|
||||
layerSettings : {[layerId: string]: {[key: string]: number|boolean}} = {};
|
||||
layerIds : string[] = [];
|
||||
layerSettings: { [layerId: string]: { [key: string]: number | boolean } } = {};
|
||||
layerIds: string[] = [];
|
||||
globalFill = true;
|
||||
globalOpacity = 1.0;
|
||||
|
||||
framePrimitives : Primitive[][] = []; // The primitives for each frame [frameIndex]
|
||||
framePrimitives: Primitive[][] = []; // The primitives for each frame [frameIndex]
|
||||
|
||||
currentPrimitives : Primitive[] = null; // The primitives for the currently displayed frame
|
||||
currentPrimitives: Primitive[] = null; // The primitives for the currently displayed frame
|
||||
|
||||
frameCoordinates : Float32Array[] = []; // The coordinates for each frame [frameIndex]
|
||||
frameCoordinates: Float32Array[] = []; // The coordinates for each frame [frameIndex]
|
||||
|
||||
scale : number;
|
||||
focusPoint : vec2;
|
||||
initFocusPoint : vec2;
|
||||
center: vec2;
|
||||
scale: number;
|
||||
angle: number;
|
||||
focusPoint: vec2;
|
||||
focusPointDelta: vec2;
|
||||
transform: mat3;
|
||||
|
||||
constructor(canvasId : string, public frameRate : number, public width : number, public height : number, objectCache : ObjectCache, public SetStatus : (status : string) => void, public SetWarning : (message : string) => void, public RequestRedraw : () => void, public ReportFrameIdChange : (canvasId : string, frameId : string) => void)
|
||||
{
|
||||
constructor(canvasId: string, public frameRate: number, public width: number, public height: number, objectCache: ObjectCache, public SetStatus: (status: string) => void, public SetWarning: (message: string) => void, public RequestRedraw: () => void, public ReportFrameIdChange: (canvasId: string, frameId: string) => void) {
|
||||
// Base class constructor
|
||||
super(canvasId, frameRate, width, height, objectCache, SetStatus, SetWarning, RequestRedraw, ReportFrameIdChange);
|
||||
|
||||
|
@ -279,6 +255,8 @@ export default class Canvas2D extends CanvasBase
|
|||
this.context = this.htmlCanvas.getContext('2d');
|
||||
|
||||
this.backgroundStyle = "#000000";
|
||||
this.center = vec2.fromValues(width, height);
|
||||
vec2.scale(this.center, this.center, window.devicePixelRatio / 2);
|
||||
|
||||
this.ResetView();
|
||||
|
||||
|
@ -287,15 +265,14 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
|
||||
// Adds UI for the control of certain layers
|
||||
SetLayerSettings(layerSettings : any)
|
||||
{
|
||||
SetLayerSettings(layerSettings: any) {
|
||||
// Delete any previous controls
|
||||
while (this.dropdownTable.childElementCount > 2)
|
||||
this.dropdownTable.removeChild(this.dropdownTable.lastChild);
|
||||
|
||||
this.layerSettings = layerSettings;
|
||||
this.layerIds = [null];
|
||||
for(let layerId in layerSettings){
|
||||
for (let layerId in layerSettings) {
|
||||
this.layerIds.push(layerId);
|
||||
}
|
||||
|
||||
|
@ -303,8 +280,7 @@ export default class Canvas2D extends CanvasBase
|
|||
var BorderStyle = "1px solid #cccccc";
|
||||
var headerRow = document.createElement("tr");
|
||||
headerRow.style.borderBottom = BorderStyle;
|
||||
for(var headerName of ["Visible", "Opacity", "Layer Id"])
|
||||
{
|
||||
for (var headerName of ["Visible", "Opacity", "Layer Id"]) {
|
||||
var headerItem = document.createElement("th");
|
||||
headerItem.className = "scenepic-dropdown-header";
|
||||
headerItem.innerHTML = headerName;
|
||||
|
@ -313,8 +289,7 @@ export default class Canvas2D extends CanvasBase
|
|||
this.dropdownTable.appendChild(headerRow);
|
||||
|
||||
// Create row helper function
|
||||
var createRow = (id, label) =>
|
||||
{
|
||||
var createRow = (id, label) => {
|
||||
// Create fill checkbox
|
||||
var checkboxFill = document.createElement("input");
|
||||
checkboxFill.type = "checkbox";
|
||||
|
@ -338,14 +313,12 @@ export default class Canvas2D extends CanvasBase
|
|||
return [checkboxFill, sliderOpacity, labelLayer];
|
||||
};
|
||||
|
||||
var addRow = (rowItems, border) =>
|
||||
{
|
||||
var addRow = (rowItems, border) => {
|
||||
// Create table row
|
||||
var tr = document.createElement("tr");
|
||||
if (border)
|
||||
tr.style.borderTop = BorderStyle;
|
||||
var addControl = (el, className) =>
|
||||
{
|
||||
var addControl = (el, className) => {
|
||||
var td = document.createElement("td");
|
||||
td.appendChild(el);
|
||||
td.className = className;
|
||||
|
@ -362,8 +335,7 @@ export default class Canvas2D extends CanvasBase
|
|||
addRow(createRow("<<<GLOBAL>>>", "Global"), true);
|
||||
}
|
||||
|
||||
ShowLayerFilled(layerId : string) : boolean
|
||||
{
|
||||
ShowLayerFilled(layerId: string): boolean {
|
||||
if (layerId == "<<<GLOBAL>>>")
|
||||
return this.globalFill;
|
||||
if (layerId == null)
|
||||
|
@ -375,28 +347,24 @@ export default class Canvas2D extends CanvasBase
|
|||
return true;
|
||||
}
|
||||
|
||||
ToggleLayerFilled(index: number)
|
||||
{
|
||||
if(index >= this.layerIds.length) {
|
||||
ToggleLayerFilled(index: number) {
|
||||
if (index >= this.layerIds.length) {
|
||||
return
|
||||
}
|
||||
|
||||
let layerId = this.layerIds[index];
|
||||
let filled = this.ShowLayerFilled(layerId);
|
||||
this.SetLayerFilled(layerId, !filled);
|
||||
}
|
||||
}
|
||||
|
||||
SetLayerFilled(layerId : string, filled : boolean)
|
||||
{
|
||||
SetLayerFilled(layerId: string, filled: boolean) {
|
||||
if (layerId == null)
|
||||
return;
|
||||
|
||||
if (layerId == "<<<GLOBAL>>>")
|
||||
{
|
||||
if (layerId == "<<<GLOBAL>>>") {
|
||||
this.globalFill = filled;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
if (!(layerId in this.layerSettings))
|
||||
this.layerSettings[layerId] = {};
|
||||
|
||||
|
@ -407,8 +375,7 @@ export default class Canvas2D extends CanvasBase
|
|||
this.PrepareBuffers();
|
||||
}
|
||||
|
||||
GetLayerOpacity(layerId : string) : number
|
||||
{
|
||||
GetLayerOpacity(layerId: string): number {
|
||||
if (layerId == "<<<GLOBAL>>>")
|
||||
return this.globalOpacity;
|
||||
if (layerId == null || !(layerId in this.layerSettings) || !("opacity" in this.layerSettings[layerId]))
|
||||
|
@ -417,17 +384,14 @@ export default class Canvas2D extends CanvasBase
|
|||
return <number>this.layerSettings[layerId]["opacity"];
|
||||
}
|
||||
|
||||
SetLayerOpacity(layerId : string, opacity : number)
|
||||
{
|
||||
SetLayerOpacity(layerId: string, opacity: number) {
|
||||
if (layerId == null)
|
||||
return;
|
||||
|
||||
if (layerId == "<<<GLOBAL>>>")
|
||||
{
|
||||
if (layerId == "<<<GLOBAL>>>") {
|
||||
this.globalOpacity = opacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
if (!(layerId in this.layerSettings))
|
||||
this.layerSettings[layerId] = {};
|
||||
this.layerSettings[layerId]["opacity"] = opacity;
|
||||
|
@ -435,10 +399,9 @@ export default class Canvas2D extends CanvasBase
|
|||
|
||||
// Prepare buffers and force redraw
|
||||
this.PrepareBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
GetLayerRenderOrder(layerId : string) : number
|
||||
{
|
||||
GetLayerRenderOrder(layerId: string): number {
|
||||
if (layerId == null || !(layerId in this.layerSettings) || !("renderOrder" in this.layerSettings[layerId]))
|
||||
return -1e3; // Backmost layer - bit of a hack
|
||||
else
|
||||
|
@ -446,10 +409,8 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
|
||||
// Execute a single canvas command
|
||||
ExecuteCanvasCommand(command : any)
|
||||
{
|
||||
switch(command["CommandType"])
|
||||
{
|
||||
ExecuteCanvasCommand(command: any) {
|
||||
switch (command["CommandType"]) {
|
||||
case "SetBackgroundStyle":
|
||||
this.backgroundStyle = String(command["Value"]);
|
||||
break;
|
||||
|
@ -470,32 +431,31 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
}
|
||||
|
||||
AddPrimitive(frameIndex : number, primitive : Primitive)
|
||||
{
|
||||
if(primitive.layerId == null){
|
||||
AddPrimitive(frameIndex: number, primitive: Primitive) {
|
||||
if (primitive.layerId == null) {
|
||||
this.framePrimitives[frameIndex].push(primitive);
|
||||
}else{
|
||||
if(!(primitive.layerId in this.layerSettings)){
|
||||
} else {
|
||||
if (!(primitive.layerId in this.layerSettings)) {
|
||||
this.layerSettings[primitive.layerId] = {};
|
||||
this.SetLayerSettings(this.layerSettings);
|
||||
}
|
||||
|
||||
let renderOrder = this.GetLayerRenderOrder(primitive.layerId);
|
||||
let index = 0;
|
||||
let lastId : string = null;
|
||||
for(let other of this.framePrimitives[frameIndex]){
|
||||
if(other.layerId == lastId){
|
||||
let lastId: string = null;
|
||||
for (let other of this.framePrimitives[frameIndex]) {
|
||||
if (other.layerId == lastId) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(other.layerId == primitive.layerId){
|
||||
|
||||
if (other.layerId == primitive.layerId) {
|
||||
break;
|
||||
}
|
||||
|
||||
lastId = other.layerId;
|
||||
let otherOrder = this.GetLayerRenderOrder(other.layerId);
|
||||
if(renderOrder < otherOrder){
|
||||
if (renderOrder < otherOrder) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -505,41 +465,37 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
|
||||
if (this.currentFrameIndex == frameIndex)
|
||||
this.PrepareBuffers();
|
||||
this.PrepareBuffers();
|
||||
}
|
||||
|
||||
SetCoordinates(frameIndex: number, coordinates: Float32Array)
|
||||
{
|
||||
SetCoordinates(frameIndex: number, coordinates: Float32Array) {
|
||||
this.frameCoordinates[frameIndex] = coordinates;
|
||||
}
|
||||
|
||||
GetCoordinate(frameIndex: number, index: number)
|
||||
{
|
||||
GetCoordinate(frameIndex: number, index: number) {
|
||||
let start = index * 2;
|
||||
let end = start + 2;
|
||||
return this.frameCoordinates[frameIndex].slice(start, end);
|
||||
}
|
||||
|
||||
AddLines(frameIndex: number, lines: Uint16Array, style: Uint8Array, width: Float32Array, layerIds: [string, number][])
|
||||
{
|
||||
AddLines(frameIndex: number, lines: Uint16Array, style: Uint8Array, width: Float32Array, layerIds: [string, number][]) {
|
||||
let numLines = width.length;
|
||||
let layerId = null;
|
||||
let nextLayer = numLines;
|
||||
let currentLayer = 0;
|
||||
if(layerIds != null){
|
||||
if (layerIds != null) {
|
||||
layerId = layerIds[0][0];
|
||||
nextLayer = currentLayer + layerIds[0][1];
|
||||
}
|
||||
|
||||
for(let i=0; i<numLines; ++i)
|
||||
{
|
||||
let info = lines.slice(i*3, i*3 + 3);
|
||||
let coordinates = this.frameCoordinates[frameIndex].slice(info[0]*2, info[1]*2);
|
||||
for (let i = 0; i < numLines; ++i) {
|
||||
let info = lines.slice(i * 3, i * 3 + 3);
|
||||
let coordinates = this.frameCoordinates[frameIndex].slice(info[0] * 2, info[1] * 2);
|
||||
let closePath = info[2] == 1;
|
||||
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i*8, i*8 + 4));
|
||||
let fillStyle = Misc.StyleToHtmlHex(style.slice(i*8 + 4, i*8 + 8));
|
||||
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i * 8, i * 8 + 4));
|
||||
let fillStyle = Misc.StyleToHtmlHex(style.slice(i * 8 + 4, i * 8 + 8));
|
||||
let lineWidth = width[i];
|
||||
if(i == nextLayer){
|
||||
if (i == nextLayer) {
|
||||
currentLayer += 1;
|
||||
layerId = layerIds[currentLayer][0];
|
||||
nextLayer = i + layerIds[currentLayer][1]
|
||||
|
@ -549,26 +505,24 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
}
|
||||
|
||||
AddCircles(frameIndex: number, circles: Float32Array, style: Uint8Array, layerIds: [string, number][])
|
||||
{
|
||||
AddCircles(frameIndex: number, circles: Float32Array, style: Uint8Array, layerIds: [string, number][]) {
|
||||
let numCircles = style.length / 8;
|
||||
let layerId = null;
|
||||
let nextLayer = numCircles;
|
||||
let currentLayer = 0;
|
||||
if(layerIds != null){
|
||||
if (layerIds != null) {
|
||||
layerId = layerIds[0][0];
|
||||
nextLayer = currentLayer + layerIds[0][1];
|
||||
}
|
||||
|
||||
for(let i=0; i<numCircles; ++i)
|
||||
{
|
||||
let info = circles.slice(i*4, i*4 + 4);
|
||||
for (let i = 0; i < numCircles; ++i) {
|
||||
let info = circles.slice(i * 4, i * 4 + 4);
|
||||
let center = info.slice(0, 2);
|
||||
let radius = info[2];
|
||||
let lineWidth = info[3];
|
||||
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i*8, i*8 + 4));
|
||||
let fillStyle = Misc.StyleToHtmlHex(style.slice(i*8 + 4, i*8 + 8));
|
||||
if(i == nextLayer){
|
||||
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i * 8, i * 8 + 4));
|
||||
let fillStyle = Misc.StyleToHtmlHex(style.slice(i * 8 + 4, i * 8 + 8));
|
||||
if (i == nextLayer) {
|
||||
currentLayer += 1;
|
||||
layerId = layerIds[currentLayer][0];
|
||||
nextLayer = i + layerIds[currentLayer][1]
|
||||
|
@ -579,10 +533,8 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
|
||||
// Execute a single frame command
|
||||
ExecuteFrameCommand(command : any, frameIndex : number)
|
||||
{
|
||||
switch(command["CommandType"])
|
||||
{
|
||||
ExecuteFrameCommand(command: any, frameIndex: number) {
|
||||
switch (command["CommandType"]) {
|
||||
case "SetCoordinates":
|
||||
var coordinates = Misc.Base64ToFloat32Array(command["CoordinateBuffer"])
|
||||
this.SetCoordinates(frameIndex, coordinates);
|
||||
|
@ -640,24 +592,20 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
}
|
||||
|
||||
AllocateFrame()
|
||||
{
|
||||
AllocateFrame() {
|
||||
this.framePrimitives.push([]);
|
||||
this.frameCoordinates.push(null);
|
||||
}
|
||||
|
||||
DeallocateFrame(frameIndex : number)
|
||||
{
|
||||
DeallocateFrame(frameIndex: number) {
|
||||
this.framePrimitives[frameIndex] = [];
|
||||
this.frameCoordinates[frameIndex] = null;
|
||||
}
|
||||
|
||||
NotifyTextureUpdated(textureId : string)
|
||||
{
|
||||
NotifyTextureUpdated(textureId: string) {
|
||||
}
|
||||
|
||||
PrepareBuffers() : void
|
||||
{
|
||||
PrepareBuffers(): void {
|
||||
super.PrepareBuffers();
|
||||
|
||||
var currentFrameIndex = this.currentFrameIndex;
|
||||
|
@ -666,15 +614,12 @@ export default class Canvas2D extends CanvasBase
|
|||
var newPrimitives = this.framePrimitives[currentFrameIndex].slice();
|
||||
|
||||
// Deal with image caching (only applies if primitives are actually ImagePrimitives or VideoPrimitives)
|
||||
for(var primitive of newPrimitives)
|
||||
{
|
||||
for (var primitive of newPrimitives) {
|
||||
var cacheId = primitive.GetObjectCacheId();
|
||||
if (cacheId != null) this.objectCache.AddUser(cacheId);
|
||||
}
|
||||
if (this.currentPrimitives != null)
|
||||
{
|
||||
for(var primitive of this.currentPrimitives)
|
||||
{
|
||||
if (this.currentPrimitives != null) {
|
||||
for (var primitive of this.currentPrimitives) {
|
||||
var cacheId = primitive.GetObjectCacheId();
|
||||
if (cacheId != null) this.objectCache.RemoveUser(cacheId);
|
||||
}
|
||||
|
@ -684,63 +629,66 @@ export default class Canvas2D extends CanvasBase
|
|||
this.currentPrimitives = newPrimitives;
|
||||
}
|
||||
|
||||
ResetView() : void
|
||||
{
|
||||
this.focusPoint = vec2.fromValues(this.width / 2, this.height / 2);
|
||||
ResetView(): void {
|
||||
this.focusPointDelta = vec2.create();
|
||||
this.focusPoint = vec2.copy(vec2.create(), this.center);
|
||||
this.scale = 1;
|
||||
this.angle = 0;
|
||||
this.updateTransform();
|
||||
this.setTransform();
|
||||
}
|
||||
|
||||
private getTransform() : mat3
|
||||
{
|
||||
const focusPixel = vec2.fromValues(
|
||||
this.htmlCanvas.clientWidth / 2,
|
||||
this.htmlCanvas.clientHeight / 2)
|
||||
ComputeCameraTwist(point: vec2, event: PointerEvent) {
|
||||
const old = this.pointerCoords.get(event.pointerId);
|
||||
if (old == undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const translate = vec2.scale(vec2.create(), this.focusPoint, this.scale);
|
||||
vec2.subtract(translate, focusPixel, translate);
|
||||
|
||||
return mat3.fromValues(
|
||||
this.scale, 0, 0,
|
||||
0, this.scale, 0,
|
||||
translate[0], translate[1], 1)
|
||||
const dx = point[0] - old[0];
|
||||
const dy = old[1] - point[1];
|
||||
let delta = dx;
|
||||
if (Math.abs(dy) > Math.abs(dx)) {
|
||||
delta = dy;
|
||||
}
|
||||
return 2 * delta / (window.devicePixelRatio * this.htmlCanvas.clientWidth);
|
||||
}
|
||||
|
||||
private setTransform() : void
|
||||
{
|
||||
let transform = this.getTransform();
|
||||
private updateTransform() {
|
||||
const toFocus = mat3.fromTranslation(mat3.create(), this.focusPoint);
|
||||
const fromCenter = mat3.fromTranslation(mat3.create(), vec2.negate(vec2.create(), this.center));
|
||||
const scale = mat3.fromScaling(mat3.create(), vec2.fromValues(this.scale, this.scale));
|
||||
const rotate = mat3.fromRotation(mat3.create(), this.angle);
|
||||
|
||||
this.transform = mat3.create();
|
||||
mat3.multiply(this.transform, fromCenter, this.transform);
|
||||
mat3.multiply(this.transform, scale, this.transform);
|
||||
mat3.multiply(this.transform, rotate, this.transform);
|
||||
mat3.multiply(this.transform, toFocus, this.transform);
|
||||
}
|
||||
|
||||
private setTransform(): void {
|
||||
this.context.setTransform(
|
||||
transform[0], transform[1],
|
||||
transform[3], transform[4],
|
||||
transform[6], transform[7])
|
||||
this.transform[0], this.transform[1],
|
||||
this.transform[3], this.transform[4],
|
||||
this.transform[6], this.transform[7])
|
||||
}
|
||||
|
||||
// Render scene method
|
||||
Render()
|
||||
{
|
||||
Render() {
|
||||
// Clear
|
||||
this.context.fillStyle = this.backgroundStyle;
|
||||
const inv = mat3.invert(mat3.create(), this.getTransform());
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
this.context.resetTransform();
|
||||
let width = this.htmlCanvas.clientWidth * window.devicePixelRatio;
|
||||
let height = this.htmlCanvas.clientHeight * window.devicePixelRatio;
|
||||
if(inv[0] > 1){
|
||||
const tl = vec2.transformMat3(vec2.create(), vec2.fromValues(0, 0), inv);
|
||||
const br = vec2.transformMat3(vec2.create(), vec2.fromValues(width, height), inv);
|
||||
x = tl[0];
|
||||
y = tl[1];
|
||||
width = br[0] - x;
|
||||
height = br[1] - y;
|
||||
}
|
||||
this.context.fillRect(x, y, width, height);
|
||||
this.context.fillRect(0, 0, width, height);
|
||||
this.setTransform();
|
||||
|
||||
// Composite primitives
|
||||
if (this.currentPrimitives == null) return;
|
||||
for(var primitive of this.currentPrimitives){
|
||||
for (var primitive of this.currentPrimitives) {
|
||||
let opacity = this.GetLayerOpacity(primitive.layerId) * this.globalOpacity;
|
||||
let filled = this.ShowLayerFilled(primitive.layerId) && this.globalFill;
|
||||
if(!filled || opacity == 0.0){
|
||||
if (!filled || opacity == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -750,14 +698,12 @@ export default class Canvas2D extends CanvasBase
|
|||
}
|
||||
}
|
||||
|
||||
StartPlaying()
|
||||
{
|
||||
StartPlaying() {
|
||||
// pre-cache
|
||||
for(let primitives of this.framePrimitives){
|
||||
for(let primitive of primitives){
|
||||
for (let primitives of this.framePrimitives) {
|
||||
for (let primitive of primitives) {
|
||||
let cacheId = primitive.GetObjectCacheId();
|
||||
if(cacheId != null)
|
||||
{
|
||||
if (cacheId != null) {
|
||||
this.objectCache.AddUser(cacheId);
|
||||
}
|
||||
}
|
||||
|
@ -766,35 +712,31 @@ export default class Canvas2D extends CanvasBase
|
|||
super.StartPlaying();
|
||||
}
|
||||
|
||||
StopPlaying()
|
||||
{
|
||||
super.StopPlaying();
|
||||
StopPlaying() {
|
||||
super.StopPlaying();
|
||||
|
||||
// free up the cache
|
||||
for(let primitives of this.framePrimitives){
|
||||
for(let primitive of primitives){
|
||||
for (let primitives of this.framePrimitives) {
|
||||
for (let primitive of primitives) {
|
||||
let imageId = primitive.GetObjectCacheId();
|
||||
if(imageId != null)
|
||||
{
|
||||
if (imageId != null) {
|
||||
this.objectCache.RemoveUser(imageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandleKeyDown(key : string) : [boolean, boolean]
|
||||
{
|
||||
HandleKeyDown(key: string): [boolean, boolean] {
|
||||
var result = super.HandleKeyDown(key);
|
||||
if (result[0]) return result; // Already handled
|
||||
|
||||
let handled = true;
|
||||
key = key.toLowerCase();
|
||||
switch(key)
|
||||
{
|
||||
switch (key) {
|
||||
case "r":
|
||||
this.ResetView();
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
|
@ -803,32 +745,72 @@ export default class Canvas2D extends CanvasBase
|
|||
return [handled, false];
|
||||
}
|
||||
|
||||
HandlePointerDown(point: vec2, event: PointerEvent) : void {
|
||||
this.initFocusPoint = vec2.copy(vec2.create(), this.focusPoint);
|
||||
super.HandlePointerDown(point, event);
|
||||
pointerCenter(): vec2 {
|
||||
if (this.pointerCoords.size == 1) {
|
||||
return this.pointerCoords.get(this.pid0);
|
||||
} else {
|
||||
const p0 = this.pointerCoords.get(this.pid0);
|
||||
const p1 = this.pointerCoords.get(this.pid1);
|
||||
let center = vec2.add(vec2.create(), p0, p1);
|
||||
vec2.scale(center, center, 0.5);
|
||||
return center;
|
||||
}
|
||||
}
|
||||
|
||||
HandlePointerMove(point: vec2, event: PointerEvent): void {
|
||||
updateFocusPointDelta(): void {
|
||||
if (this.pointerCoords.size == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const center = this.pointerCenter();
|
||||
const focusPoint = vec2.scale(vec2.create(), this.focusPoint, 1 / window.devicePixelRatio);
|
||||
this.focusPointDelta = vec2.subtract(vec2.create(), focusPoint, center);
|
||||
}
|
||||
|
||||
HandlePointerDown(point: vec2, event: PointerEvent): void {
|
||||
super.HandlePointerDown(point, event);
|
||||
|
||||
this.updateFocusPointDelta();
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
HandlePointerUp(event: PointerEvent): void {
|
||||
super.HandlePointerUp(event);
|
||||
|
||||
this.updateFocusPointDelta();
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
HandlePointerMoveWithTwist(point: vec2, twistAngle: number, event: PointerEvent): void {
|
||||
const countPointers = this.pointerCoords.size;
|
||||
if (countPointers == 0) return;
|
||||
if (countPointers == 0 || countPointers > 2) return;
|
||||
|
||||
const init = this.initPointerCoords.get(event.pointerId);
|
||||
const transform = this.getTransform();
|
||||
const inv = mat3.invert(mat3.create(), transform);
|
||||
if (countPointers == 2) {
|
||||
const pinchZoom = this.PinchZoom(point, event);
|
||||
this.scale = this.scale * pinchZoom.distanceRatio;
|
||||
this.angle = this.angle + pinchZoom.angleDelta;
|
||||
|
||||
const source = vec2.transformMat3(vec2.create(), init, inv);
|
||||
const dest = vec2.transformMat3(vec2.create(), point, inv);
|
||||
const delta = vec2.subtract(vec2.create(), source, dest);
|
||||
|
||||
vec2.add(this.focusPoint, this.initFocusPoint, delta);
|
||||
// Set focus to mean of two points
|
||||
vec2.add(this.focusPoint, pinchZoom.center, this.focusPointDelta);
|
||||
vec2.scale(this.focusPoint, this.focusPoint, window.devicePixelRatio);
|
||||
} else {
|
||||
this.angle += twistAngle;
|
||||
|
||||
if (twistAngle == 0) {
|
||||
vec2.add(this.focusPoint, point, this.focusPointDelta);
|
||||
vec2.scale(this.focusPoint, this.focusPoint, window.devicePixelRatio);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateTransform();
|
||||
this.setTransform();
|
||||
|
||||
super.HandlePointerMove(point, event);
|
||||
}
|
||||
|
||||
HandleMouseWheel(event: WheelEvent): void {
|
||||
const factor = Math.pow(1.1, event.deltaY > 0 ? -1 : 1);
|
||||
this.scale *= factor;
|
||||
this.updateTransform();
|
||||
this.setTransform();
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,14 +1,12 @@
|
|||
|
||||
export default class DropDownMenu
|
||||
{
|
||||
title : string;
|
||||
header : HTMLSpanElement;
|
||||
wrapper : HTMLDivElement; // Contains both contents and inputBox
|
||||
contents : HTMLSpanElement;
|
||||
dropDownMenu : HTMLSelectElement;
|
||||
export default class DropDownMenu {
|
||||
title: string;
|
||||
header: HTMLSpanElement;
|
||||
wrapper: HTMLDivElement; // Contains both contents and inputBox
|
||||
contents: HTMLSpanElement;
|
||||
dropDownMenu: HTMLSelectElement;
|
||||
|
||||
constructor(title : string, style : string, parent : HTMLElement)
|
||||
{
|
||||
constructor(title: string, style: string, parent: HTMLElement) {
|
||||
// Create div
|
||||
var div = document.createElement("div");
|
||||
div.className = "scenepic"; // CSS style class name
|
||||
|
@ -40,10 +38,8 @@ export default class DropDownMenu
|
|||
parent.appendChild(div);
|
||||
}
|
||||
|
||||
SetItems(items : string[])
|
||||
{
|
||||
items.forEach(item =>
|
||||
{
|
||||
SetItems(items: string[]) {
|
||||
items.forEach(item => {
|
||||
// Create table row
|
||||
var option = document.createElement("option");
|
||||
option.value = item;
|
||||
|
@ -54,24 +50,20 @@ export default class DropDownMenu
|
|||
this.Update();
|
||||
}
|
||||
|
||||
SetItemDisabled(index : number, disabled : boolean)
|
||||
{
|
||||
SetItemDisabled(index: number, disabled: boolean) {
|
||||
this.dropDownMenu.options[index].disabled = disabled;
|
||||
}
|
||||
|
||||
SetSelection(index : number)
|
||||
{
|
||||
SetSelection(index: number) {
|
||||
this.dropDownMenu.selectedIndex = index;
|
||||
}
|
||||
|
||||
SetTitle(htmlTitle : string)
|
||||
{
|
||||
SetTitle(htmlTitle: string) {
|
||||
this.title = htmlTitle;
|
||||
this.Update();
|
||||
}
|
||||
|
||||
Update()
|
||||
{
|
||||
Update() {
|
||||
this.header.style.display = (this.title == null || this.title == "") ? "none" : "";
|
||||
this.header.innerHTML = this.title;
|
||||
this.wrapper.style.display = "inline-block";
|
||||
|
|
|
@ -29,19 +29,19 @@ class Rect {
|
|||
this.height = height;
|
||||
}
|
||||
|
||||
get topEdge() : number {
|
||||
get topEdge(): number {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
get bottomEdge() : number {
|
||||
get bottomEdge(): number {
|
||||
return this.y + this.height;
|
||||
}
|
||||
|
||||
get leftEdge() : number {
|
||||
get leftEdge(): number {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
get rightEdge() : number {
|
||||
get rightEdge(): number {
|
||||
return this.x + this.width;
|
||||
}
|
||||
|
||||
|
@ -93,27 +93,27 @@ class Layout {
|
|||
width: number;
|
||||
height: number;
|
||||
|
||||
get topEdge() : number {
|
||||
get topEdge(): number {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
get bottomEdge() : number {
|
||||
get bottomEdge(): number {
|
||||
return this.y + this.height;
|
||||
}
|
||||
|
||||
get leftEdge() : number {
|
||||
get leftEdge(): number {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
get rightEdge() : number {
|
||||
get rightEdge(): number {
|
||||
return this.x + this.width;
|
||||
}
|
||||
|
||||
constructor({top = new Rect(),
|
||||
right = new Rect(),
|
||||
bottom = new Rect(),
|
||||
left = new Rect(),
|
||||
center = new Rect()} : {top? : Rect | Layout, right? : Rect | Layout, bottom? : Rect | Layout, left? : Rect | Layout, center? : Rect | Layout} = {}) {
|
||||
constructor({ top = new Rect(),
|
||||
right = new Rect(),
|
||||
bottom = new Rect(),
|
||||
left = new Rect(),
|
||||
center = new Rect() }: { top?: Rect | Layout, right?: Rect | Layout, bottom?: Rect | Layout, left?: Rect | Layout, center?: Rect | Layout } = {}) {
|
||||
this.x = this.y = this.width = this.height = 0;
|
||||
this.top = top;
|
||||
this.right = right;
|
||||
|
@ -404,7 +404,7 @@ export default class Graph extends CanvasBase {
|
|||
let layout = new Layout();
|
||||
|
||||
let nameRect = new Rect(0, 0, nameColumnWidth, nameHeight);
|
||||
let nameLayout = new Layout({left: nameRect});
|
||||
let nameLayout = new Layout({ left: nameRect });
|
||||
switch (this.nameAlign) {
|
||||
case "top":
|
||||
layout.top = nameLayout;
|
||||
|
@ -426,34 +426,34 @@ export default class Graph extends CanvasBase {
|
|||
let valueRect = new Rect(0, 0, valueColumnWidth, valueHeight);
|
||||
switch (this.valueAlign) {
|
||||
case "top":
|
||||
if(layout.top instanceof Layout){
|
||||
if (layout.top instanceof Layout) {
|
||||
layout.top.right = valueRect;
|
||||
}else{
|
||||
layout.top = new Layout({right: valueRect})
|
||||
} else {
|
||||
layout.top = new Layout({ right: valueRect })
|
||||
}
|
||||
break;
|
||||
|
||||
case "bottom":
|
||||
if(layout.bottom instanceof Layout){
|
||||
if (layout.bottom instanceof Layout) {
|
||||
layout.bottom.right = valueRect;
|
||||
}else{
|
||||
layout.bottom = new Layout({right: valueRect})
|
||||
} else {
|
||||
layout.bottom = new Layout({ right: valueRect })
|
||||
}
|
||||
break;
|
||||
|
||||
case "left":
|
||||
if(layout.left instanceof Layout){
|
||||
if (layout.left instanceof Layout) {
|
||||
layout.left.right = valueRect;
|
||||
}else{
|
||||
layout.left = new Layout({right: valueRect})
|
||||
} else {
|
||||
layout.left = new Layout({ right: valueRect })
|
||||
}
|
||||
break;
|
||||
|
||||
case "right":
|
||||
if(layout.right instanceof Layout){
|
||||
if (layout.right instanceof Layout) {
|
||||
layout.right.right = valueRect;
|
||||
}else{
|
||||
layout.right = new Layout({right: valueRect})
|
||||
} else {
|
||||
layout.right = new Layout({ right: valueRect })
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
188
tssrc/Mesh.ts
188
tssrc/Mesh.ts
|
@ -1,53 +1,51 @@
|
|||
// NB ONLY TO BE USED INTERNALLY, NOT IN API
|
||||
import Misc from "./Misc"
|
||||
import {vec3, vec4, mat4} from "gl-matrix";
|
||||
import { vec3, vec4, mat4 } from "gl-matrix";
|
||||
import { VertexBuffer, VertexBufferType } from "./VertexBuffers";
|
||||
|
||||
export default class Mesh
|
||||
{
|
||||
static readonly ElementsPerTriangle : number = 3;
|
||||
static readonly ElementsPerLine : number = 2;
|
||||
ElementsPerVertex : number;
|
||||
VertexOffsetPosition : number = 0;
|
||||
VertexOffsetNormal : number = 3;
|
||||
VertexOffsetColor : number = 0; // Dummy value - simplifies logic in webglmeshbuffers
|
||||
VertexOffsetTexture : number = 0; // Dummy value - simplifies logic in webglmeshbuffers
|
||||
export default class Mesh {
|
||||
static readonly ElementsPerTriangle: number = 3;
|
||||
static readonly ElementsPerLine: number = 2;
|
||||
ElementsPerVertex: number;
|
||||
VertexOffsetPosition: number = 0;
|
||||
VertexOffsetNormal: number = 3;
|
||||
VertexOffsetColor: number = 0; // Dummy value - simplifies logic in webglmeshbuffers
|
||||
VertexOffsetTexture: number = 0; // Dummy value - simplifies logic in webglmeshbuffers
|
||||
|
||||
ElementsPerInstance : number;
|
||||
InstanceOffsetPosition : number = 0;
|
||||
InstanceOffsetRotation : number = 0;
|
||||
InstanceOffsetColor : number = 0;
|
||||
ElementsPerInstance: number;
|
||||
InstanceOffsetPosition: number = 0;
|
||||
InstanceOffsetRotation: number = 0;
|
||||
InstanceOffsetColor: number = 0;
|
||||
|
||||
// Host memory buffers
|
||||
centerOfMass : vec3; // Mean of vertex positions, to allow depth ordering for improved transparency
|
||||
vertexBuffer : Float32Array; // Vertex.Length elements per vertex
|
||||
bytesPerIndex : number; // 2 or 4
|
||||
triangleBuffer : ArrayBuffer; // 3 elements (v1, v2, v3) per triangle
|
||||
lineBuffer : ArrayBuffer; // 2 elements (v1, v2) per line
|
||||
wireframeEdgeBuffer : ArrayBuffer; // 2 elements (v1, v2) per edge, used for wireframe
|
||||
instanceBuffer : Float32Array; // Used for instancing
|
||||
instanceBufferHasRotations : boolean;
|
||||
instanceBufferHasColors : boolean;
|
||||
color : vec3; // Either null (per-vertex coloring stored in vertex buffer) or a 3-vector (shared color across whole mesh)
|
||||
textureId : string;
|
||||
isBillboard : boolean; // If true, zero out rotation
|
||||
isLabel : boolean; // If true, treat the mesh specially as a label
|
||||
nnTexture : boolean; // If true, render textures using nearest neighbor interpolation, otherwise linear
|
||||
useTextureAlpha : boolean; // If true, use the texture's alpha channel for transparency. Otherwise treat as opaque.
|
||||
cameraSpace : boolean; // If true, the mesh is defined in camera-space rather than world space and will thus not move with mouse commands
|
||||
layerId : string; // Unique identifier for a layer of Meshes with shared properties
|
||||
doubleSided : boolean; // If true, then back-face culling is turned off
|
||||
centerOfMass: vec3; // Mean of vertex positions, to allow depth ordering for improved transparency
|
||||
vertexBuffer: Float32Array; // Vertex.Length elements per vertex
|
||||
bytesPerIndex: number; // 2 or 4
|
||||
triangleBuffer: ArrayBuffer; // 3 elements (v1, v2, v3) per triangle
|
||||
lineBuffer: ArrayBuffer; // 2 elements (v1, v2) per line
|
||||
wireframeEdgeBuffer: ArrayBuffer; // 2 elements (v1, v2) per edge, used for wireframe
|
||||
instanceBuffer: Float32Array; // Used for instancing
|
||||
instanceBufferHasRotations: boolean;
|
||||
instanceBufferHasColors: boolean;
|
||||
color: vec3; // Either null (per-vertex coloring stored in vertex buffer) or a 3-vector (shared color across whole mesh)
|
||||
textureId: string;
|
||||
isBillboard: boolean; // If true, zero out rotation
|
||||
isLabel: boolean; // If true, treat the mesh specially as a label
|
||||
nnTexture: boolean; // If true, render textures using nearest neighbor interpolation, otherwise linear
|
||||
useTextureAlpha: boolean; // If true, use the texture's alpha channel for transparency. Otherwise treat as opaque.
|
||||
cameraSpace: boolean; // If true, the mesh is defined in camera-space rather than world space and will thus not move with mouse commands
|
||||
layerId: string; // Unique identifier for a layer of Meshes with shared properties
|
||||
doubleSided: boolean; // If true, then back-face culling is turned off
|
||||
|
||||
// Properties
|
||||
CountVertices() : number { return this.vertexBuffer.length / this.ElementsPerVertex; }
|
||||
CountTriangles() : number { return this.triangleBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerTriangle); }
|
||||
CountLines() : number { return this.lineBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine); }
|
||||
CountWireframeEdges() : number { return this.wireframeEdgeBuffer != null ? this.wireframeEdgeBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine) : 0; }
|
||||
CountInstances() : number { return this.instanceBuffer != null ? (this.instanceBuffer.length / this.ElementsPerInstance) : 1; }
|
||||
CountVertices(): number { return this.vertexBuffer.length / this.ElementsPerVertex; }
|
||||
CountTriangles(): number { return this.triangleBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerTriangle); }
|
||||
CountLines(): number { return this.lineBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine); }
|
||||
CountWireframeEdges(): number { return this.wireframeEdgeBuffer != null ? this.wireframeEdgeBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine) : 0; }
|
||||
CountInstances(): number { return this.instanceBuffer != null ? (this.instanceBuffer.length / this.ElementsPerInstance) : 1; }
|
||||
|
||||
// Constructor
|
||||
constructor(vertexBuffer : Float32Array, bytesPerIndex : number, triangleBuffer : ArrayBuffer, lineBuffer : ArrayBuffer, color : vec3, textureId : string = null, nnTexture : boolean = true, useTextureAlpha = true, instanceBuffer : Float32Array = null, instanceBufferHasRotations = false, instanceBufferHasColors = false)
|
||||
{
|
||||
constructor(vertexBuffer: Float32Array, bytesPerIndex: number, triangleBuffer: ArrayBuffer, lineBuffer: ArrayBuffer, color: vec3, textureId: string = null, nnTexture: boolean = true, useTextureAlpha = true, instanceBuffer: Float32Array = null, instanceBufferHasRotations = false, instanceBufferHasColors = false) {
|
||||
this.vertexBuffer = vertexBuffer;
|
||||
this.bytesPerIndex = bytesPerIndex;
|
||||
this.triangleBuffer = triangleBuffer;
|
||||
|
@ -63,19 +61,17 @@ export default class Mesh
|
|||
this.layerId = null;
|
||||
|
||||
// Deal with instance buffer
|
||||
if (instanceBuffer == null)
|
||||
{
|
||||
if (instanceBuffer == null) {
|
||||
this.instanceBuffer = null;
|
||||
this.instanceBufferHasRotations = false;
|
||||
this.instanceBufferHasColors = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
this.instanceBuffer = instanceBuffer;
|
||||
this.instanceBufferHasRotations = instanceBufferHasRotations;
|
||||
this.instanceBufferHasColors = instanceBufferHasColors;
|
||||
}
|
||||
|
||||
|
||||
// Compute number of elements per vertex
|
||||
this.ElementsPerVertex = 6; // Position and normal: X,Y,Z,NX,NY,NZ
|
||||
if (this.color == null) // I.e. per-vertex rather than per-mesh color
|
||||
|
@ -83,31 +79,26 @@ export default class Mesh
|
|||
this.VertexOffsetColor = this.ElementsPerVertex;
|
||||
this.ElementsPerVertex += 3; // Per-vertex color: R,G,B
|
||||
}
|
||||
if (this.textureId != null)
|
||||
{
|
||||
if (this.textureId != null) {
|
||||
this.VertexOffsetTexture = this.ElementsPerVertex;
|
||||
this.ElementsPerVertex += 2; // Texture coords: U,V
|
||||
}
|
||||
|
||||
// Compute number of elements per instance
|
||||
this.ElementsPerInstance = 3; // Translation: X,Y,Z
|
||||
if (this.instanceBufferHasRotations)
|
||||
{
|
||||
if (this.instanceBufferHasRotations) {
|
||||
this.InstanceOffsetRotation = this.ElementsPerInstance;
|
||||
this.ElementsPerInstance += 4; // Quaternion rotation: W,X,Y,Z
|
||||
}
|
||||
if (this.instanceBufferHasColors)
|
||||
{
|
||||
if (this.instanceBufferHasColors) {
|
||||
this.InstanceOffsetColor = this.ElementsPerInstance;
|
||||
this.ElementsPerInstance += 3; // Color: R,G,B
|
||||
}
|
||||
|
||||
// Compute center of mass
|
||||
this.centerOfMass = vec3.create();
|
||||
if (this.CountVertices() > 0)
|
||||
{
|
||||
for(var i = 0; i < this.vertexBuffer.length; i += this.ElementsPerVertex)
|
||||
{
|
||||
if (this.CountVertices() > 0) {
|
||||
for (var i = 0; i < this.vertexBuffer.length; i += this.ElementsPerVertex) {
|
||||
this.centerOfMass[0] += this.vertexBuffer[i + 0];
|
||||
this.centerOfMass[1] += this.vertexBuffer[i + 1];
|
||||
this.centerOfMass[2] += this.vertexBuffer[i + 2];
|
||||
|
@ -118,32 +109,28 @@ export default class Mesh
|
|||
}
|
||||
}
|
||||
|
||||
Update(buffer : Float32Array, update_flags: VertexBufferType) : Mesh
|
||||
{
|
||||
if(this.CountInstances() > 1)
|
||||
{
|
||||
Update(buffer: Float32Array, update_flags: VertexBufferType): Mesh {
|
||||
if (this.CountInstances() > 1) {
|
||||
return this.ToInstanceBuffer(buffer, update_flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
return this.ToVertexBuffer(buffer, update_flags);
|
||||
}
|
||||
}
|
||||
|
||||
ToInstanceBuffer(update : Float32Array, updateFlags: VertexBufferType) : Mesh
|
||||
{
|
||||
ToInstanceBuffer(update: Float32Array, updateFlags: VertexBufferType): Mesh {
|
||||
let instanceBuffer = VertexBuffer.Update(this.instanceBuffer, this.ElementsPerInstance, update, updateFlags);
|
||||
let mesh = new Mesh(this.vertexBuffer,
|
||||
this.bytesPerIndex,
|
||||
this.triangleBuffer,
|
||||
this.lineBuffer,
|
||||
this.color,
|
||||
this.textureId,
|
||||
this.nnTexture,
|
||||
this.useTextureAlpha,
|
||||
instanceBuffer,
|
||||
this.instanceBufferHasRotations,
|
||||
this.instanceBufferHasColors)
|
||||
this.bytesPerIndex,
|
||||
this.triangleBuffer,
|
||||
this.lineBuffer,
|
||||
this.color,
|
||||
this.textureId,
|
||||
this.nnTexture,
|
||||
this.useTextureAlpha,
|
||||
instanceBuffer,
|
||||
this.instanceBufferHasRotations,
|
||||
this.instanceBufferHasColors)
|
||||
mesh.cameraSpace = this.cameraSpace;
|
||||
mesh.layerId = this.layerId;
|
||||
mesh.doubleSided = this.doubleSided;
|
||||
|
@ -152,20 +139,19 @@ export default class Mesh
|
|||
return mesh;
|
||||
}
|
||||
|
||||
ToVertexBuffer(update : Float32Array, updateFlags: VertexBufferType) : Mesh
|
||||
{
|
||||
ToVertexBuffer(update: Float32Array, updateFlags: VertexBufferType): Mesh {
|
||||
let vertexBuffer = VertexBuffer.Update(this.vertexBuffer, this.ElementsPerVertex, update, updateFlags);
|
||||
let mesh = new Mesh(vertexBuffer,
|
||||
this.bytesPerIndex,
|
||||
this.triangleBuffer,
|
||||
this.lineBuffer,
|
||||
this.color,
|
||||
this.textureId,
|
||||
this.nnTexture,
|
||||
this.useTextureAlpha,
|
||||
this.instanceBuffer,
|
||||
this.instanceBufferHasRotations,
|
||||
this.instanceBufferHasColors)
|
||||
this.bytesPerIndex,
|
||||
this.triangleBuffer,
|
||||
this.lineBuffer,
|
||||
this.color,
|
||||
this.textureId,
|
||||
this.nnTexture,
|
||||
this.useTextureAlpha,
|
||||
this.instanceBuffer,
|
||||
this.instanceBufferHasRotations,
|
||||
this.instanceBufferHasColors)
|
||||
mesh.cameraSpace = this.cameraSpace;
|
||||
mesh.layerId = this.layerId;
|
||||
mesh.doubleSided = this.doubleSided;
|
||||
|
@ -174,15 +160,13 @@ export default class Mesh
|
|||
return mesh;
|
||||
}
|
||||
|
||||
static Parse(definition : any)
|
||||
{
|
||||
var color : vec3 = null;
|
||||
var textureId : string = null;
|
||||
var nnTexture : boolean = true;
|
||||
var useTextureAlpha : boolean = true;
|
||||
static Parse(definition: any) {
|
||||
var color: vec3 = null;
|
||||
var textureId: string = null;
|
||||
var nnTexture: boolean = true;
|
||||
var useTextureAlpha: boolean = true;
|
||||
|
||||
switch(definition["PrimitiveType"])
|
||||
{
|
||||
switch (definition["PrimitiveType"]) {
|
||||
case "SingleColorMesh":
|
||||
color = <vec3>Misc.Base64ToFloat32Array(definition["Color"]);
|
||||
textureId = Misc.GetDefault(definition, "TextureId", null);
|
||||
|
@ -191,9 +175,8 @@ export default class Mesh
|
|||
case "MultiColorMesh":
|
||||
let vertexBuffer = Misc.Base64ToFloat32Array(definition["VertexBuffer"]);
|
||||
var indexBufferType = definition["IndexBufferType"];
|
||||
var bytesPerIndex : number, triangleBuffer : ArrayBuffer, lineBuffer : ArrayBuffer;
|
||||
if (indexBufferType == "UInt16")
|
||||
{
|
||||
var bytesPerIndex: number, triangleBuffer: ArrayBuffer, lineBuffer: ArrayBuffer;
|
||||
if (indexBufferType == "UInt16") {
|
||||
bytesPerIndex = 2;
|
||||
triangleBuffer = Misc.Base64ToUInt16Array(definition["TriangleBuffer"]).buffer;
|
||||
lineBuffer = Misc.Base64ToUInt16Array(definition["LineBuffer"]).buffer;
|
||||
|
@ -209,20 +192,17 @@ export default class Mesh
|
|||
var instanceBufferHasColors = Misc.GetDefault(definition, "InstanceBufferHasColors", false);
|
||||
return new Mesh(vertexBuffer, bytesPerIndex, triangleBuffer, lineBuffer, color, textureId, nnTexture, useTextureAlpha, instanceBuffer, instanceBufferHasRotations, instanceBufferHasColors);
|
||||
default:
|
||||
throw "Unknown \"Primitive\" type in mesh: " + JSON.stringify(definition);
|
||||
throw "Unknown \"Primitive\" type in mesh: " + JSON.stringify(definition);
|
||||
}
|
||||
}
|
||||
|
||||
GetWireframeEdgeBuffer()
|
||||
{
|
||||
if (this.wireframeEdgeBuffer == null)
|
||||
{
|
||||
GetWireframeEdgeBuffer() {
|
||||
if (this.wireframeEdgeBuffer == null) {
|
||||
this.wireframeEdgeBuffer = new ArrayBuffer(this.triangleBuffer.byteLength * 2);
|
||||
var triangleView : any = this.bytesPerIndex == 2 ? new Uint16Array(this.triangleBuffer) : new Uint32Array(this.triangleBuffer);
|
||||
var wireframeView : any = this.bytesPerIndex == 2 ? new Uint16Array(this.wireframeEdgeBuffer) : new Uint32Array(this.wireframeEdgeBuffer);
|
||||
var triangleView: any = this.bytesPerIndex == 2 ? new Uint16Array(this.triangleBuffer) : new Uint32Array(this.triangleBuffer);
|
||||
var wireframeView: any = this.bytesPerIndex == 2 ? new Uint16Array(this.wireframeEdgeBuffer) : new Uint32Array(this.wireframeEdgeBuffer);
|
||||
|
||||
for(var i = 0; i < triangleView.length; i += 3)
|
||||
{
|
||||
for (var i = 0; i < triangleView.length; i += 3) {
|
||||
wireframeView[2 * i + 5] = wireframeView[2 * i + 0] = triangleView[i + 0];
|
||||
wireframeView[2 * i + 2] = wireframeView[2 * i + 1] = triangleView[i + 1];
|
||||
wireframeView[2 * i + 4] = wireframeView[2 * i + 3] = triangleView[i + 2];
|
||||
|
|
|
@ -4,11 +4,11 @@ import ShaderProgram from "./Shaders";
|
|||
import WebGLMeshBuffers from "./WebGLMeshBuffers";
|
||||
|
||||
export class MeshPicker {
|
||||
readonly frameBuffer : WebGLFramebuffer;
|
||||
readonly program : ShaderProgram;
|
||||
data : Uint8Array;
|
||||
readonly frameBuffer: WebGLFramebuffer;
|
||||
readonly program: ShaderProgram;
|
||||
data: Uint8Array;
|
||||
|
||||
constructor(gl : WebGL2RenderingContext, width: number, height: number){
|
||||
constructor(gl: WebGL2RenderingContext, width: number, height: number) {
|
||||
this.program = new ShaderProgram(gl, "pickerVertex", "pickerFragment");
|
||||
|
||||
let targetTexture = Misc.AssertNotNull(gl.createTexture(), "WebGL returned a null texture");
|
||||
|
@ -17,9 +17,9 @@ export class MeshPicker {
|
|||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
||||
width, height, 0,
|
||||
gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
|
||||
width, height, 0,
|
||||
gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
|
||||
let depthBuffer = Misc.AssertNotNull(gl.createRenderbuffer(), "WebGL returned a null render buffer");
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
|
||||
|
@ -28,14 +28,14 @@ export class MeshPicker {
|
|||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
|
||||
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
|
||||
|
||||
this.data = new Uint8Array(4);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
}
|
||||
|
||||
Pick(gl : WebGL2RenderingContext, meshes: [WebGLMeshBuffers, mat4][], point: vec2, v2sMatrix: mat4) : number {
|
||||
Pick(gl: WebGL2RenderingContext, meshes: [WebGLMeshBuffers, mat4][], point: vec2, v2sMatrix: mat4): number {
|
||||
const canvas = <HTMLCanvasElement>gl.canvas;
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
|
||||
|
@ -50,7 +50,7 @@ export class MeshPicker {
|
|||
const program = this.program;
|
||||
gl.useProgram(program.program);
|
||||
|
||||
meshes.forEach(function([buffer, w2vMatrix]) {
|
||||
meshes.forEach(function ([buffer, w2vMatrix]) {
|
||||
buffer.RenderPicker(program, v2sMatrix, w2vMatrix)
|
||||
});
|
||||
|
||||
|
|
110
tssrc/Misc.ts
110
tssrc/Misc.ts
|
@ -1,60 +1,50 @@
|
|||
import {mat3, mat4, vec3, vec4, quat} from "gl-matrix";
|
||||
import { mat3, mat4, vec3, vec4, quat } from "gl-matrix";
|
||||
import * as pako from "pako";
|
||||
|
||||
export default class Misc
|
||||
{
|
||||
static Sign(value: number): number
|
||||
{
|
||||
if(value < 0)
|
||||
{
|
||||
export default class Misc {
|
||||
static Sign(value: number): number {
|
||||
if (value < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(value > 0)
|
||||
{
|
||||
|
||||
if (value > 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Mat3FromMat4(m4 : mat4) : mat3
|
||||
{
|
||||
static Mat3FromMat4(m4: mat4): mat3 {
|
||||
var m3 = mat3.create();
|
||||
mat3.fromMat4(m3, m4);
|
||||
return m3;
|
||||
}
|
||||
|
||||
static NormalizeVec3InPlace(vec : Float32Array, ind : number)
|
||||
{
|
||||
var norm = 1.0/Math.sqrt(vec[ind] * vec[ind] + vec[ind+1] * vec[ind+1] + vec[ind+2] * vec[ind+2]);
|
||||
static NormalizeVec3InPlace(vec: Float32Array, ind: number) {
|
||||
var norm = 1.0 / Math.sqrt(vec[ind] * vec[ind] + vec[ind + 1] * vec[ind + 1] + vec[ind + 2] * vec[ind + 2]);
|
||||
vec[ind] *= norm;
|
||||
vec[ind+1] *= norm;
|
||||
vec[ind+2] *= norm;
|
||||
vec[ind + 1] *= norm;
|
||||
vec[ind + 2] *= norm;
|
||||
}
|
||||
|
||||
static GetDefault(obj, name, defaultValue) : any
|
||||
{
|
||||
static GetDefault(obj, name, defaultValue): any {
|
||||
if (name in obj)
|
||||
return obj[name];
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
static PushOrCreateArray(obj, key, value)
|
||||
{
|
||||
static PushOrCreateArray(obj, key, value) {
|
||||
if (!(key in obj))
|
||||
obj[key] = [];
|
||||
obj[key].push(value);
|
||||
}
|
||||
|
||||
static DecoderArray : any = null;
|
||||
static DecoderArray: any = null;
|
||||
|
||||
static Base64ToArrayBuffer(base64str : string)
|
||||
{
|
||||
static Base64ToArrayBuffer(base64str: string) {
|
||||
// Initialize decoder array if necessary (not threadsafe)
|
||||
if (Misc.DecoderArray == null)
|
||||
{
|
||||
if (Misc.DecoderArray == null) {
|
||||
const CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
Misc.DecoderArray = {};
|
||||
for (var i = 0; i < CODES.length; i++)
|
||||
|
@ -70,8 +60,7 @@ export default class Misc
|
|||
var aView = new Uint8Array(aBuff);
|
||||
|
||||
// Decode
|
||||
for(var i = 0, j = 0; i < countBytes; i += 3)
|
||||
{
|
||||
for (var i = 0, j = 0; i < countBytes; i += 3) {
|
||||
// Get characters
|
||||
var c0 = base64str.charCodeAt(j++);
|
||||
var c1 = base64str.charCodeAt(j++);
|
||||
|
@ -85,11 +74,11 @@ export default class Misc
|
|||
var e3 = Misc.DecoderArray[c3];
|
||||
|
||||
aView[i] = (e0 << 2) | (e1 >> 4);
|
||||
if (e2 != 64) aView[i+1] = ((e1 & 15) << 4) | (e2 >> 2);
|
||||
if (e3 != 64) aView[i+2] = ((e2 & 3) << 6) | e3;
|
||||
if (e2 != 64) aView[i + 1] = ((e1 & 15) << 4) | (e2 >> 2);
|
||||
if (e3 != 64) aView[i + 2] = ((e2 & 3) << 6) | e3;
|
||||
}
|
||||
|
||||
let result : Uint8Array;
|
||||
let result: Uint8Array;
|
||||
try {
|
||||
result = pako.inflate(aView);
|
||||
} catch (err) {
|
||||
|
@ -99,29 +88,25 @@ export default class Misc
|
|||
return result.buffer;
|
||||
}
|
||||
|
||||
static DataUrlToBlob(dataUrl: string) : Blob
|
||||
{
|
||||
static DataUrlToBlob(dataUrl: string): Blob {
|
||||
let parts = dataUrl.split(',');
|
||||
let mime = parts[0].match(/:(.*?);/)[1];
|
||||
let array = new Uint8Array(Misc.Base64ToArrayBuffer(parts[1]))
|
||||
return new Blob([array], {"type": mime});
|
||||
return new Blob([array], { "type": mime });
|
||||
}
|
||||
|
||||
static Base64ToBlob(base64: string, mime: string) : Blob
|
||||
{
|
||||
static Base64ToBlob(base64: string, mime: string): Blob {
|
||||
let array = new Uint8Array(Misc.Base64ToArrayBuffer(base64));
|
||||
return new Blob([array], {"type": mime});
|
||||
return new Blob([array], { "type": mime });
|
||||
}
|
||||
|
||||
static DataUrlToArrayBuffer(dataUrl: string) : ArrayBuffer
|
||||
{
|
||||
static DataUrlToArrayBuffer(dataUrl: string): ArrayBuffer {
|
||||
let parts = dataUrl.split(',');
|
||||
return Misc.Base64ToArrayBuffer(parts[1]);
|
||||
}
|
||||
|
||||
// Convert either from Base64 string or from regular array to Float32Array
|
||||
static Base64ToFloat32Array(obj : any)
|
||||
{
|
||||
static Base64ToFloat32Array(obj: any) {
|
||||
if (obj == null) return null;
|
||||
if (typeof obj == "string")
|
||||
return new Float32Array(Misc.Base64ToArrayBuffer(obj));
|
||||
|
@ -129,8 +114,7 @@ export default class Misc
|
|||
return new Float32Array(obj);
|
||||
}
|
||||
|
||||
static Base64ToUInt8Array(obj: any)
|
||||
{
|
||||
static Base64ToUInt8Array(obj: any) {
|
||||
if (obj == null) return null;
|
||||
if (typeof obj == "string")
|
||||
return new Uint8Array(Misc.Base64ToArrayBuffer(obj));
|
||||
|
@ -139,8 +123,7 @@ export default class Misc
|
|||
}
|
||||
|
||||
// Convert either from Base64 string or from regular array to Int16Array
|
||||
static Base64ToUInt16Array(obj : any)
|
||||
{
|
||||
static Base64ToUInt16Array(obj: any) {
|
||||
if (obj == null) return null;
|
||||
if (typeof obj == "string")
|
||||
return new Uint16Array(Misc.Base64ToArrayBuffer(obj));
|
||||
|
@ -149,8 +132,7 @@ export default class Misc
|
|||
}
|
||||
|
||||
// Convert either from Base64 string or from regular array to Int32Array
|
||||
static Base64ToUInt32Array(obj : any)
|
||||
{
|
||||
static Base64ToUInt32Array(obj: any) {
|
||||
if (obj == null) return null;
|
||||
if (typeof obj == "string")
|
||||
return new Uint32Array(Misc.Base64ToArrayBuffer(obj));
|
||||
|
@ -158,24 +140,20 @@ export default class Misc
|
|||
return new Uint32Array(obj);
|
||||
}
|
||||
|
||||
static GetSearchValue(name : string)
|
||||
{
|
||||
static GetSearchValue(name: string) {
|
||||
var searchStr = location.search.substring(1);
|
||||
var vars = searchStr.split('&');
|
||||
for (var i = 0; i < vars.length; i++)
|
||||
{
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split('=');
|
||||
if (decodeURIComponent(pair[0]) == name)
|
||||
{
|
||||
if (decodeURIComponent(pair[0]) == name) {
|
||||
return decodeURIComponent(pair[1]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static StyleToHtmlHex(style: Uint8Array) : string | null
|
||||
{
|
||||
if(style[0] == 1){
|
||||
static StyleToHtmlHex(style: Uint8Array): string | null {
|
||||
if (style[0] == 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -185,24 +163,22 @@ export default class Misc
|
|||
return "#" + Misc.Byte2Hex(red) + Misc.Byte2Hex(green) + Misc.Byte2Hex(blue);
|
||||
}
|
||||
|
||||
static Byte2Hex (value: number) : string
|
||||
{
|
||||
const hex_string = "0123456789ABCDEF";
|
||||
return String(hex_string.substr((value >> 4) & 0x0F,1)) + hex_string.substr(value & 0x0F,1);
|
||||
static Byte2Hex(value: number): string {
|
||||
const hex_string = "0123456789ABCDEF";
|
||||
return String(hex_string.substr((value >> 4) & 0x0F, 1)) + hex_string.substr(value & 0x0F, 1);
|
||||
}
|
||||
|
||||
static IsPow2(val : number)
|
||||
{
|
||||
static IsPow2(val: number) {
|
||||
var l = Math.log(val) / Math.log(2);
|
||||
return l == Math.floor(l);
|
||||
}
|
||||
|
||||
static AssertNotNull<T>(value: T | null, error: string) : T {
|
||||
if(value == null){
|
||||
static AssertNotNull<T>(value: T | null, error: string): T {
|
||||
if (value == null) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
513
tssrc/SPScene.ts
513
tssrc/SPScene.ts
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,18 +1,18 @@
|
|||
import SPScene from "./SPScene"
|
||||
|
||||
|
||||
function scenepic(id: string, commands: any, wait=100){
|
||||
let element : HTMLElement | null = null;
|
||||
if(id != null){
|
||||
function scenepic(id: string, commands: any, wait = 100) {
|
||||
let element: HTMLElement | null = null;
|
||||
if (id != null) {
|
||||
element = document.getElementById(id);
|
||||
if(element == null){
|
||||
if (element == null) {
|
||||
console.warn(id + " does not exist in the DOM, retrying");
|
||||
setTimeout(() => { scenepic(id, commands, wait * 2); }, wait);
|
||||
}else{
|
||||
} else {
|
||||
let scene = new SPScene(element);
|
||||
scene.ExecuteSceneCommands(commands);
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
let scene = new SPScene();
|
||||
scene.ExecuteSceneCommands(commands);
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ void main()
|
|||
"pickerVertex": {
|
||||
type: "x-shader/x-vertex",
|
||||
script:
|
||||
`#version 300 es
|
||||
`#version 300 es
|
||||
// Vertex inputs
|
||||
in vec3 vertexPositionIn;
|
||||
|
||||
|
@ -224,8 +224,8 @@ void main()
|
|||
`},
|
||||
"pickerFragment": {
|
||||
type: "x-shader/x-fragment",
|
||||
script:
|
||||
`#version 300 es
|
||||
script:
|
||||
`#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
uniform int u_id;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
|
||||
export default class TextPanel
|
||||
{
|
||||
minimized : boolean;
|
||||
title : string;
|
||||
header : HTMLSpanElement;
|
||||
wrapper : HTMLDivElement; // Contains both contents and inputBox
|
||||
contents : HTMLSpanElement;
|
||||
inputBox : HTMLTextAreaElement;
|
||||
export default class TextPanel {
|
||||
minimized: boolean;
|
||||
title: string;
|
||||
header: HTMLSpanElement;
|
||||
wrapper: HTMLDivElement; // Contains both contents and inputBox
|
||||
contents: HTMLSpanElement;
|
||||
inputBox: HTMLTextAreaElement;
|
||||
|
||||
constructor(id : string, title : string, style : string, parent : HTMLElement, startMinimized = false, addInputBox = false)
|
||||
{
|
||||
constructor(id: string, title: string, style: string, parent: HTMLElement, startMinimized = false, addInputBox = false) {
|
||||
// Create div
|
||||
var div = document.createElement("div");
|
||||
div.className = "scenepic"; // CSS style class name
|
||||
|
@ -50,30 +48,26 @@ export default class TextPanel
|
|||
parent.appendChild(div);
|
||||
}
|
||||
|
||||
SetContents(htmlContents : string, append = false)
|
||||
{
|
||||
SetContents(htmlContents: string, append = false) {
|
||||
if (append)
|
||||
this.contents.innerHTML += htmlContents;
|
||||
else
|
||||
this.contents.innerHTML = htmlContents;
|
||||
}
|
||||
|
||||
SetTitle(htmlTitle : string)
|
||||
{
|
||||
SetTitle(htmlTitle: string) {
|
||||
this.title = htmlTitle;
|
||||
this.Update();
|
||||
}
|
||||
|
||||
Update()
|
||||
{
|
||||
Update() {
|
||||
this.header.style.display = (this.title == null || this.title == "") ? "none" : "";
|
||||
this.header.innerHTML = this.title + (this.minimized ? " ▽" : " ▷");
|
||||
var newDisplayValue = this.minimized ? "none" : "inline-block";
|
||||
this.wrapper.style.display = newDisplayValue;
|
||||
}
|
||||
|
||||
SetInputBoxContents(htmlContents : string)
|
||||
{
|
||||
SetInputBoxContents(htmlContents: string) {
|
||||
this.inputBox.value = htmlContents;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +1,66 @@
|
|||
export const enum VertexBufferType
|
||||
{
|
||||
export const enum VertexBufferType {
|
||||
None = 0,
|
||||
Positions = 1,
|
||||
Normals = 2,
|
||||
Colors = 4,
|
||||
Rotations = 8
|
||||
Rotations = 8
|
||||
};
|
||||
|
||||
export class VertexBuffer {
|
||||
private static partialCopy(buffer: Float32Array, bufferStride: number,
|
||||
update: Float32Array, updateOffset: number, updateSize: number) : Float32Array
|
||||
{
|
||||
update: Float32Array, updateOffset: number, updateSize: number): Float32Array {
|
||||
buffer = Float32Array.from(buffer);
|
||||
for(let b_i=0, u_i=0; b_i < buffer.length; b_i += bufferStride, u_i += updateSize)
|
||||
{
|
||||
for (let b_i = 0, u_i = 0; b_i < buffer.length; b_i += bufferStride, u_i += updateSize) {
|
||||
buffer.set(update.slice(u_i, u_i + updateSize), b_i + updateOffset);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static copyPosition(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
if(elementsPerRow == 3)
|
||||
{
|
||||
private static copyPosition(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
if (elementsPerRow == 3) {
|
||||
return update;
|
||||
}
|
||||
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 0, 3);
|
||||
}
|
||||
|
||||
private static copyNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
private static copyNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 3, 3);
|
||||
}
|
||||
|
||||
private static copyRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
private static copyRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 3, 4);
|
||||
}
|
||||
|
||||
private static copyColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
return this.partialCopy(buffer, elementsPerRow, update, elementsPerRow - 3, 3);
|
||||
private static copyColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
return this.partialCopy(buffer, elementsPerRow, update, elementsPerRow - 3, 3);
|
||||
}
|
||||
|
||||
private static copyPositionRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
if(elementsPerRow == 7)
|
||||
{
|
||||
private static copyPositionRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
if (elementsPerRow == 7) {
|
||||
return update;
|
||||
}
|
||||
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 0, 7);
|
||||
}
|
||||
|
||||
private static copyPositionNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
if(elementsPerRow == 6)
|
||||
{
|
||||
private static copyPositionNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
if (elementsPerRow == 6) {
|
||||
return update;
|
||||
}
|
||||
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 0, 6);
|
||||
}
|
||||
|
||||
private static copyPositionColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
if(elementsPerRow == 6)
|
||||
{
|
||||
private static copyPositionColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
if (elementsPerRow == 6) {
|
||||
return update;
|
||||
}
|
||||
|
||||
let offset = elementsPerRow - 3;
|
||||
buffer = Float32Array.from(buffer);
|
||||
for(let b_i=0, u_i=0; b_i < buffer.length; b_i += elementsPerRow, u_i += 6)
|
||||
{
|
||||
for (let b_i = 0, u_i = 0; b_i < buffer.length; b_i += elementsPerRow, u_i += 6) {
|
||||
buffer.set(update.slice(u_i, u_i + 3), b_i);
|
||||
buffer.set(update.slice(u_i + 3, u_i + 6), b_i + offset);
|
||||
}
|
||||
|
@ -83,50 +68,46 @@ export class VertexBuffer {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
private static copyRotationColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
private static copyRotationColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 3, 7);
|
||||
}
|
||||
|
||||
private static copyNormalColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
|
||||
{
|
||||
private static copyNormalColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
|
||||
return this.partialCopy(buffer, elementsPerRow, update, 3, 6);
|
||||
}
|
||||
|
||||
static Update(buffer: Float32Array, elementsPerRow: number, update: Float32Array, updateFlags: VertexBufferType) : Float32Array
|
||||
{
|
||||
switch(updateFlags)
|
||||
{
|
||||
static Update(buffer: Float32Array, elementsPerRow: number, update: Float32Array, updateFlags: VertexBufferType): Float32Array {
|
||||
switch (updateFlags) {
|
||||
case VertexBufferType.Positions:
|
||||
return this.copyPosition(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Normals:
|
||||
return this.copyNormal(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Rotations:
|
||||
return this.copyRotation(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Colors:
|
||||
return this.copyColor(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Positions | VertexBufferType.Rotations:
|
||||
return this.copyPositionRotation(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Positions | VertexBufferType.Normals:
|
||||
return this.copyPositionNormal(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Positions | VertexBufferType.Colors:
|
||||
return this.copyPositionColor(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Rotations | VertexBufferType.Colors:
|
||||
return this.copyRotationColor(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Normals | VertexBufferType.Colors:
|
||||
return this.copyNormalColor(buffer, update, elementsPerRow);
|
||||
|
||||
|
||||
case VertexBufferType.Positions | VertexBufferType.Normals | VertexBufferType.Colors:
|
||||
return update;
|
||||
|
||||
|
||||
case VertexBufferType.Positions | VertexBufferType.Rotations | VertexBufferType.Colors:
|
||||
return update;
|
||||
}
|
||||
|
|
|
@ -1,47 +1,45 @@
|
|||
import {quat, vec3, mat3, mat4} from "gl-matrix";
|
||||
import { quat, vec3, mat3, mat4 } from "gl-matrix";
|
||||
import Mesh from "./Mesh";
|
||||
import Misc from "./Misc"
|
||||
import ShaderProgram from "./Shaders";
|
||||
|
||||
export default class WebGLMeshBuffers
|
||||
{
|
||||
gl : WebGL2RenderingContext;
|
||||
sp : ShaderProgram;
|
||||
m : Mesh;
|
||||
export default class WebGLMeshBuffers {
|
||||
gl: WebGL2RenderingContext;
|
||||
sp: ShaderProgram;
|
||||
m: Mesh;
|
||||
|
||||
color : vec3;
|
||||
color: vec3;
|
||||
|
||||
vertexDataBuffer : WebGLBuffer;
|
||||
triangleIndexBuffer : WebGLBuffer;
|
||||
lineIndexBuffer : WebGLBuffer;
|
||||
wireframeEdgeIndexBuffer : WebGLBuffer;
|
||||
instanceDataBuffer : WebGLBuffer;
|
||||
vertexDataBuffer: WebGLBuffer;
|
||||
triangleIndexBuffer: WebGLBuffer;
|
||||
lineIndexBuffer: WebGLBuffer;
|
||||
wireframeEdgeIndexBuffer: WebGLBuffer;
|
||||
instanceDataBuffer: WebGLBuffer;
|
||||
|
||||
hasTexture : boolean;
|
||||
texture : WebGLTexture;
|
||||
|
||||
indexType : number;
|
||||
hasTexture: boolean;
|
||||
texture: WebGLTexture;
|
||||
|
||||
m2wMatrix : mat4;
|
||||
indexType: number;
|
||||
|
||||
id : number;
|
||||
m2wMatrix: mat4;
|
||||
|
||||
id: number;
|
||||
|
||||
// Used for label computations
|
||||
labelWidthNormalized : number = -1.0;
|
||||
labelHeightNormalized : number = -1.0;
|
||||
labelTranslateScreenX : number = 0.0;
|
||||
labelTranslateScreenY : number = 0.0;
|
||||
labelTranslateWorldX : number = 0.0;
|
||||
labelTranslateWorldY : number = 0.0;
|
||||
labelWidthNormalized: number = -1.0;
|
||||
labelHeightNormalized: number = -1.0;
|
||||
labelTranslateScreenX: number = 0.0;
|
||||
labelTranslateScreenY: number = 0.0;
|
||||
labelTranslateWorldX: number = 0.0;
|
||||
labelTranslateWorldY: number = 0.0;
|
||||
|
||||
constructor(gl : WebGL2RenderingContext, sp : ShaderProgram, m : Mesh, textureSrc = null)
|
||||
{
|
||||
constructor(gl: WebGL2RenderingContext, sp: ShaderProgram, m: Mesh, textureSrc = null) {
|
||||
this.gl = gl;
|
||||
this.sp = sp;
|
||||
this.m = m;
|
||||
|
||||
this.color = m.color;
|
||||
|
||||
|
||||
this.indexType = m.bytesPerIndex == 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
|
||||
|
||||
// Create and bind vertex buffer
|
||||
|
@ -85,18 +83,16 @@ export default class WebGLMeshBuffers
|
|||
else
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([1, 1, 1, 1])); // Dummy texture
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR);
|
||||
if (textureSrc != null && (Misc.IsPow2(textureSrc.width) && Misc.IsPow2(textureSrc.height)))
|
||||
{
|
||||
if (textureSrc != null && (Misc.IsPow2(textureSrc.width) && Misc.IsPow2(textureSrc.height))) {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR_MIPMAP_NEAREST)
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR);
|
||||
}
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
|
||||
|
@ -104,8 +100,7 @@ export default class WebGLMeshBuffers
|
|||
this.m2wMatrix = mat4.create();
|
||||
}
|
||||
|
||||
Finalize()
|
||||
{
|
||||
Finalize() {
|
||||
var gl = this.gl;
|
||||
gl.deleteBuffer(this.vertexDataBuffer);
|
||||
gl.deleteBuffer(this.triangleIndexBuffer);
|
||||
|
@ -117,8 +112,7 @@ export default class WebGLMeshBuffers
|
|||
gl.deleteTexture(this.texture);
|
||||
}
|
||||
|
||||
ApplyBillboardEffect(m2vMatrix : mat4)
|
||||
{
|
||||
ApplyBillboardEffect(m2vMatrix: mat4) {
|
||||
// Get rotational matrix component of m2vMatrix
|
||||
var det = mat4.determinant(m2vMatrix);
|
||||
var scale = det != 0.0 ? 1.0 / Math.pow(det, 1.0 / 3.0) : 1.0;
|
||||
|
@ -132,12 +126,11 @@ export default class WebGLMeshBuffers
|
|||
// Invert and transform
|
||||
quat.invert(q, q);
|
||||
var transform = mat4.create();
|
||||
mat4.fromRotationTranslationScaleOrigin(transform, q, [0,0,0], [1,1,1], this.m.centerOfMass);
|
||||
mat4.fromRotationTranslationScaleOrigin(transform, q, [0, 0, 0], [1, 1, 1], this.m.centerOfMass);
|
||||
mat4.multiply(m2vMatrix, m2vMatrix, transform);
|
||||
}
|
||||
|
||||
ApplyLabel(m2vMatrix : mat4, v2sMatrix : mat4)
|
||||
{
|
||||
ApplyLabel(m2vMatrix: mat4, v2sMatrix: mat4) {
|
||||
this.ApplyBillboardEffect(m2vMatrix);
|
||||
|
||||
// Helper function - careful - this references, not copies the array
|
||||
|
@ -163,23 +156,21 @@ export default class WebGLMeshBuffers
|
|||
|
||||
// Transform with alignment translation and scale factor
|
||||
var translate = vec3.fromValues(this.labelTranslateScreenX * labelDepth / v2sMatrix[0] + this.labelTranslateWorldX,
|
||||
this.labelTranslateScreenY * labelDepth / v2sMatrix[5] + this.labelTranslateWorldY,
|
||||
0.0);
|
||||
this.labelTranslateScreenY * labelDepth / v2sMatrix[5] + this.labelTranslateWorldY,
|
||||
0.0);
|
||||
var scale = vec3.fromValues(scaleFactorX, scaleFactorY, 1.0);
|
||||
var transform = mat4.create();
|
||||
mat4.fromRotationTranslationScale(transform, quat.create(), translate, scale);
|
||||
mat4.mul(m2vMatrix, m2vMatrix, transform);
|
||||
}
|
||||
|
||||
RenderBuffer(v2sMatrix : mat4, w2vMatrix : mat4, opacity : number, renderFilled : boolean, renderWireframe : boolean)
|
||||
{
|
||||
RenderBuffer(v2sMatrix: mat4, w2vMatrix: mat4, opacity: number, renderFilled: boolean, renderWireframe: boolean) {
|
||||
const gl = this.gl;
|
||||
const sp = this.sp;
|
||||
const m = this.m;
|
||||
|
||||
// Ensure we have wireframe buffer if needed
|
||||
if (renderWireframe && this.wireframeEdgeIndexBuffer == null)
|
||||
{
|
||||
if (renderWireframe && this.wireframeEdgeIndexBuffer == null) {
|
||||
this.wireframeEdgeIndexBuffer = gl.createBuffer();
|
||||
var cpuBuffer = m.GetWireframeEdgeBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.wireframeEdgeIndexBuffer);
|
||||
|
@ -199,7 +190,7 @@ export default class WebGLMeshBuffers
|
|||
|
||||
// Set model view matrix (i.e. concatenation of m2w, w2v)
|
||||
gl.uniformMatrix4fv(sp.m2vMatrixPtr, false, m2vMatrix);
|
||||
|
||||
|
||||
// Compute normals transformation matrix
|
||||
var normMatrix = Misc.Mat3FromMat4(m2vMatrix);
|
||||
mat3.invert(normMatrix, normMatrix);
|
||||
|
@ -213,71 +204,59 @@ export default class WebGLMeshBuffers
|
|||
|
||||
// Bind vertex buffers
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexDataBuffer);
|
||||
if (true)
|
||||
{
|
||||
if (true) {
|
||||
gl.enableVertexAttribArray(sp.vertexPositionAttribLoc);
|
||||
gl.vertexAttribPointer(sp.vertexPositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetPosition * 4);
|
||||
}
|
||||
if (true)
|
||||
{
|
||||
if (true) {
|
||||
gl.enableVertexAttribArray(sp.vertexNormalAttribLoc);
|
||||
gl.vertexAttribPointer(sp.vertexNormalAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetNormal * 4);
|
||||
}
|
||||
if (m.VertexOffsetColor != 0)
|
||||
{
|
||||
if (m.VertexOffsetColor != 0) {
|
||||
gl.enableVertexAttribArray(sp.vertexColorAttribLoc);
|
||||
gl.vertexAttribPointer(sp.vertexColorAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetColor * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.vertexColorAttribLoc);
|
||||
}
|
||||
if (m.VertexOffsetTexture != 0)
|
||||
{
|
||||
if (m.VertexOffsetTexture != 0) {
|
||||
gl.enableVertexAttribArray(sp.vertexTextureAttribLoc);
|
||||
gl.vertexAttribPointer(sp.vertexTextureAttribLoc, 2, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetTexture * 4); // VertexOffsetTexture = 0 when no texture used
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.vertexTextureAttribLoc);
|
||||
}
|
||||
|
||||
|
||||
// Bind instance buffers
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceDataBuffer);
|
||||
if (m.instanceBuffer != null)
|
||||
{
|
||||
if (m.instanceBuffer != null) {
|
||||
gl.enableVertexAttribArray(sp.instancePositionAttribLoc);
|
||||
gl.vertexAttribPointer(sp.instancePositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetPosition * 4);
|
||||
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.instancePositionAttribLoc);
|
||||
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 0);
|
||||
}
|
||||
if (m.instanceBufferHasRotations)
|
||||
{
|
||||
if (m.instanceBufferHasRotations) {
|
||||
gl.enableVertexAttribArray(sp.instanceRotationAttribLoc);
|
||||
gl.vertexAttribPointer(sp.instanceRotationAttribLoc, 4, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetRotation * 4);
|
||||
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.instanceRotationAttribLoc);
|
||||
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 0);
|
||||
}
|
||||
if (m.instanceBufferHasColors)
|
||||
{
|
||||
if (m.instanceBufferHasColors) {
|
||||
gl.enableVertexAttribArray(sp.instanceColorAttribLoc);
|
||||
gl.vertexAttribPointer(sp.instanceColorAttribLoc, 3, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetColor * 4);
|
||||
gl.vertexAttribDivisor(sp.instanceColorAttribLoc, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.instanceColorAttribLoc);
|
||||
gl.vertexAttribDivisor(sp.instanceColorAttribLoc, 0);
|
||||
}
|
||||
|
||||
|
||||
// Set use instance rotation uniform1i
|
||||
gl.uniform1i(sp.useInstanceRotation, m.instanceBufferHasRotations ? 1 : 0);
|
||||
|
||||
|
@ -293,8 +272,7 @@ export default class WebGLMeshBuffers
|
|||
gl.uniform1f(sp.alphaPtr, opacity);
|
||||
|
||||
// Draw triangles
|
||||
if (renderFilled)
|
||||
{
|
||||
if (renderFilled) {
|
||||
gl.uniform1i(sp.shadingTypePtr, this.m.isLabel ? 2 : (this.hasTexture ? 1 : 0));
|
||||
gl.uniform1f(sp.lightingMultiplierPtr, 1.0);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.triangleIndexBuffer);
|
||||
|
@ -302,8 +280,7 @@ export default class WebGLMeshBuffers
|
|||
}
|
||||
|
||||
// Draw wireframe edges
|
||||
if (renderWireframe)
|
||||
{
|
||||
if (renderWireframe) {
|
||||
gl.uniform1i(sp.shadingTypePtr, -1);
|
||||
gl.uniform1f(sp.lightingMultiplierPtr, opacity == 1.0 ? 0.4 : 0.5);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.wireframeEdgeIndexBuffer);
|
||||
|
@ -322,8 +299,7 @@ export default class WebGLMeshBuffers
|
|||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
}
|
||||
|
||||
RenderPicker(program: ShaderProgram, v2sMatrix : mat4, w2vMatrix : mat4)
|
||||
{
|
||||
RenderPicker(program: ShaderProgram, v2sMatrix: mat4, w2vMatrix: mat4) {
|
||||
const gl = this.gl;
|
||||
const sp = program;
|
||||
const m = this.m;
|
||||
|
@ -344,40 +320,35 @@ export default class WebGLMeshBuffers
|
|||
|
||||
// Set model view matrix (i.e. concatenation of m2w, w2v)
|
||||
gl.uniformMatrix4fv(sp.m2vMatrixPtr, false, m2vMatrix);
|
||||
|
||||
|
||||
// Bind vertex buffers
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexDataBuffer);
|
||||
if (true)
|
||||
{
|
||||
if (true) {
|
||||
gl.enableVertexAttribArray(sp.vertexPositionAttribLoc);
|
||||
gl.vertexAttribPointer(sp.vertexPositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetPosition * 4);
|
||||
}
|
||||
|
||||
|
||||
// Bind instance buffers
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceDataBuffer);
|
||||
if (m.instanceBuffer != null)
|
||||
{
|
||||
if (m.instanceBuffer != null) {
|
||||
gl.enableVertexAttribArray(sp.instancePositionAttribLoc);
|
||||
gl.vertexAttribPointer(sp.instancePositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetPosition * 4);
|
||||
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.instancePositionAttribLoc);
|
||||
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 0);
|
||||
}
|
||||
if (m.instanceBufferHasRotations)
|
||||
{
|
||||
if (m.instanceBufferHasRotations) {
|
||||
gl.enableVertexAttribArray(sp.instanceRotationAttribLoc);
|
||||
gl.vertexAttribPointer(sp.instanceRotationAttribLoc, 4, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetRotation * 4);
|
||||
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
gl.disableVertexAttribArray(sp.instanceRotationAttribLoc);
|
||||
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 0);
|
||||
}
|
||||
|
||||
|
||||
// Set use instance rotation uniform1i
|
||||
gl.uniform1i(sp.useInstanceRotation, m.instanceBufferHasRotations ? 1 : 0);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"""Script to create the holodeck assets."""
|
||||
"""Script to create the embedded TypeScript assets."""
|
||||
|
||||
import scenepic as sp
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче