Unity2021.3.10f1, URP, SRP Batcher on
I created a 10x10x10 cube matrix in the sence.
The cube is a prefab. It 1:1:1 default size, with a SRP Batcher compatible shader.
Now 5000+ Batches in runtime. How can I reduce the batches?
source: https://gist.github.com/andrew-raphael-lukasik/df4a36ff2ad89078258fd653c422a021
// scr* https://gist.github.com/andrew-raphael-lukasik/df4a36ff2ad89078258fd653c422a021
using System.Collections.Generic;
using UnityEngine;
public class GpuInstancingForGameObjects : MonoBehaviour
{
[SerializeField] Camera _camera = null;
[SerializeField] MeshRenderer[] _meshRenderers = new MeshRenderer[0];
/// <summary>
/// Prefer "true" ☑ as "false" ☐ require updates every frame.
/// It is a good idea to keep lists of still and moving mesh renderers in a separate components.
/// </summary>
public bool meshesAreStill = true;
Dictionary<(Mesh mesh,Material material),(List<Transform> transforms,Bounds aabb)> _sources = new Dictionary<(Mesh,Material),(List<Transform>,Bounds)>();
Dictionary<(Mesh mesh,Material material),(Matrix4x4[] matrices,Bounds aabb)> _batches = new Dictionary<(Mesh,Material),(Matrix4x4[],Bounds)>();
Dictionary<int,Stack<Matrix4x4[]>> _freeMatrices = new Dictionary<int,Stack<Matrix4x4[]>>();
Plane[] _frustum = new Plane[6];
void Start ()
{
Initialize();
UpdateMatrices();
if( _camera==null ) _camera = Camera.main;
if( _camera==null )
{
Debug.LogError( "no camera, can't continue" , this );
enabled = false;
}
}
void Update ()
{
if( !meshesAreStill ) UpdateMatrices();
GeometryUtility.CalculateFrustumPlanes( _camera , _frustum );
foreach( var batch in _batches )
{
var meshMaterialPair = batch.Key;
var matricesAabbPair = batch.Value;
var aabb = matricesAabbPair.aabb;
if( GeometryUtility.TestPlanesAABB(_frustum,aabb) )
{
Graphics.DrawMeshInstanced(
mesh: meshMaterialPair.mesh ,
submeshIndex: 0 ,
material: meshMaterialPair.material ,
matrices: matricesAabbPair.matrices
);
}
}
}
#if UNITY_EDITOR
// void OnDrawGizmosSelected ()
void OnDrawGizmos ()
{
Initialize();
Gizmos.color = Color.yellow;
foreach( var source in _sources )
{
var transformsAabbPair = source.Value;
var aabb = transformsAabbPair.aabb;
Gizmos.DrawWireCube( aabb.center , aabb.size );
if( Application.isPlaying && !GeometryUtility.TestPlanesAABB(_frustum,aabb) )
UnityEditor.Handles.Label( aabb.center , "(out of camera view)" );
}
}
#endif
void Initialize ()
{
_sources.Clear();
foreach( var meshRenderer in _meshRenderers )
{
if( meshRenderer==null ) continue;
var meshFilter = meshRenderer.GetComponent<MeshFilter>();
if( meshFilter==null ) continue;
var mesh = meshFilter.sharedMesh;
if( mesh==null ) continue;
foreach( var material in meshRenderer.sharedMaterials )
{
if( !material.enableInstancing && Application.isPlaying )
{
Debug.LogWarning($"\"{material.name}\" material won't be rendered as it's <b>GPU Instancing</b> is not enabled",meshRenderer);
continue;
}
if( material==null ) continue;
var aabb = meshRenderer.bounds;
var meshMaterialPair = ( mesh , material );
if( _sources.ContainsKey( meshMaterialPair ) )
{
var transforms = _sources[meshMaterialPair].transforms;
transforms.Add( meshRenderer.transform );
var newAabb = _sources[meshMaterialPair].aabb;
newAabb.Encapsulate( aabb );
_sources[meshMaterialPair] = ( transforms , newAabb );
}
else
{
_sources.Add( meshMaterialPair , ( new List<Transform>(){ meshRenderer.transform } , aabb ) );
}
}
if( Application.isPlaying )
meshRenderer.enabled = false;
}
}
void UpdateMatrices ()
{
foreach( var batch in _batches )
{
var matricesAabbPair = batch.Value;
var matrices = matricesAabbPair.matrices;
if( _freeMatrices.ContainsKey( matrices.Length ) )
{
_freeMatrices[matrices.Length].Push( matrices );
}
else
{
var stack = new Stack<Matrix4x4[]>();
stack.Push( matrices );
_freeMatrices.Add( matrices.Length , stack );
}
}
_batches.Clear();
foreach( var source in _sources )
{
var meshMaterialPair = source.Key;
var transformsAabbPair = source.Value;
var transforms = transformsAabbPair.transforms;
int numTransforms = transforms.Count;
Matrix4x4[] matrices = null;
if( _freeMatrices.ContainsKey(numTransforms) && _freeMatrices[numTransforms].Count!=0 )
{
matrices = _freeMatrices[numTransforms].Pop();
}
else matrices = new Matrix4x4[ numTransforms ];
for( int i=0 ; i<numTransforms ; i++ )
matrices[i] = transforms[i].localToWorldMatrix;
_batches.Add( meshMaterialPair , ( matrices , transformsAabbPair.aabb ) );
}
}
}
This solution provides AABB frustum culling - it is not enough to optimize your voxels-like case but a starting point. If you consider these boxes to represent voxels then your next step would be to implement a voxel occlusion culling here (to hide boxes that are inside this shape).
I am trying to write a code that inserts an Element in a doubly linked list without repetition but it's not working.
Any help would be appreciated.
This is my code:
public void insert(int value) {
Element tmp = new Element(value);
if(this.head == null) {
this.head = this.rear = tmp;
return ;
}
if(this.head.data < value) {
tmp.next = this.head;
this.head.previous = tmp;
tmp.previous = null;
this.head = tmp;
return ;
}
if(this.rear.data > value) {
tmp.previous = this.rear;
this.rear.next = tmp;
this.rear = tmp;
return ;
}
else {
Element cur = this.head;
while(cur.next != null && cur.next.data > value)
cur = cur.next;
tmp.next = cur.next;
tmp.previous = cur;
cur.next.previous = tmp;
cur.next = tmp;
}
return ;
}
You should add a second method:
public boolean isInList(int value) {
Element cur = this.head;
if(this.head == null)
return false;
while(cur != null) {
if(cur.data == value)
return true;
cur = cur.next;
}
return false;
}
And then add it to the insert method:
public void insert(int value) {
Element tmp = new Element(value);
if(this.isInList(value))
return false;
if(this.head == null) {
this.head = this.rear = tmp;
return ;
}
if(this.head.data < value) {
tmp.next = this.head;
this.head.previous = tmp;
tmp.previous = null;
this.head = tmp;
return ;
}
if(this.rear.data > value) {
tmp.previous = this.rear;
this.rear.next = tmp;
this.rear = tmp;
return ;
}
else {
Element cur = this.head;
while(cur.next != null && cur.next.data > value)
cur = cur.next;
tmp.next = cur.next;
tmp.previous = cur;
cur.next.previous = tmp;
cur.next = tmp;
}
return ;
}
I'm trying to do the following:
private void AssignPlayer()
{
EntityQuery playerQuery = GetEntityQuery(ComponentType.ReadOnly<PlayerTag>());
Entity playerEntity = playerQuery.ToEntityArray(Allocator.Temp);
Entities.
WithAll<ChaserTag>().
ForEach((ref TargetData targetData) =>
{
if (playerEntity != Entity.Null)
{
targetData.targetEntity = playerEntity;
}
}).Schedule();
}
But the line that assigns targetData.targetEntity = playerEntity; throws the error:
Cannot implicitly convert type 'Unity.Collections.NativeArray<Unity.Entities.Entity>' to 'Unity.Entities.Entity'
if( players.Length!=0 )
{
NativeArray<Entity> players = playerQuery.ToEntityArray( Allocator.TempJob );
Entities.WithName("targetData_update_job")
.WithReadOnly( players ).WithDeallocateOnJobCompletion( players )
.WithAll<ChaserTag>()
.ForEach( ( ref TargetData targetData ) =>
{
targetData.targetEntity = players[0];
}
).ScheduleParallel();
}
else Debug.LogWarning($"{nameof(AssignPlayer)}(): no players tho");
When i use this code in CKE Editor:
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
can i change this:
Image 1
to this:
Image 2
Problem: The PN Messenger in our CMS reply the last message with blockquote in blockquote. And with several replies this is very confusing.
Your Code
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
Updated
<blockquote>test</blockquote>
<blockquote>test</blockquote>
You want to display every blockquote in a new line. Try this
var array = document.getElementsByTagName('blockquote');
for(var i = 0; i < array.length; i++) {
array[i].savedContent = array[i].innerHTML;
array[i].style.margin = 0;
}
<head>
<title>Page Title</title>
</head>
<body>
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
</body>
I insert your code here (line 24) Is that correct? But it do not change.
1/**
* #license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
( function() {
function noBlockLeft( bqBlock ) {
for ( var i = 0, length = bqBlock.getChildCount(), child; i < length && ( child = bqBlock.getChild( i ) ); i++ ) {
if ( child.type == CKEDITOR.NODE_ELEMENT && child.isBlockBoundary() )
return false;
}
return true;
}
var commandObject = {
exec: function( editor ) {
var state = editor.getCommand( 'blockquote' ).state,
selection = editor.getSelection(),
range = selection && selection.getRanges()[ 0 ];
if ( !range )
return;
// Rakibul Islam
var array = document.getElementsByTagName('blockquote');
for(var i = 0; i < array.length; i++) {
array[i].savedContent = array[i].innerHTML;
array[i].style.margin = 0;
}
var bookmarks = selection.createBookmarks();
// Kludge for https://dev.ckeditor.com/ticket/1592: if the bookmark nodes are in the beginning of
// blockquote, then move them to the nearest block element in the
// blockquote.
if ( CKEDITOR.env.ie ) {
var bookmarkStart = bookmarks[ 0 ].startNode,
bookmarkEnd = bookmarks[ 0 ].endNode,
cursor;
if ( bookmarkStart && bookmarkStart.getParent().getName() == 'blockquote' ) {
cursor = bookmarkStart;
while ( ( cursor = cursor.getNext() ) ) {
if ( cursor.type == CKEDITOR.NODE_ELEMENT && cursor.isBlockBoundary() ) {
bookmarkStart.move( cursor, true );
break;
}
}
}
if ( bookmarkEnd && bookmarkEnd.getParent().getName() == 'blockquote' ) {
cursor = bookmarkEnd;
while ( ( cursor = cursor.getPrevious() ) ) {
if ( cursor.type == CKEDITOR.NODE_ELEMENT && cursor.isBlockBoundary() ) {
bookmarkEnd.move( cursor );
break;
}
}
}
}
var iterator = range.createIterator(),
block;
iterator.enlargeBr = editor.config.enterMode != CKEDITOR.ENTER_BR;
if ( state == CKEDITOR.TRISTATE_OFF ) {
var paragraphs = [];
while ( ( block = iterator.getNextParagraph() ) )
paragraphs.push( block );
// If no paragraphs, create one from the current selection position.
if ( paragraphs.length < 1 ) {
var para = editor.document.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ),
firstBookmark = bookmarks.shift();
range.insertNode( para );
para.append( new CKEDITOR.dom.text( '\ufeff', editor.document ) );
range.moveToBookmark( firstBookmark );
range.selectNodeContents( para );
range.collapse( true );
firstBookmark = range.createBookmark();
paragraphs.push( para );
bookmarks.unshift( firstBookmark );
}
// Make sure all paragraphs have the same parent.
var commonParent = paragraphs[ 0 ].getParent(),
tmp = [];
for ( var i = 0; i < paragraphs.length; i++ ) {
block = paragraphs[ i ];
commonParent = commonParent.getCommonAncestor( block.getParent() );
}
// The common parent must not be the following tags: table, tbody, tr, ol, ul.
var denyTags = { table: 1, tbody: 1, tr: 1, ol: 1, ul: 1 };
while ( denyTags[ commonParent.getName() ] )
commonParent = commonParent.getParent();
// Reconstruct the block list to be processed such that all resulting blocks
// satisfy parentNode.equals( commonParent ).
var lastBlock = null;
while ( paragraphs.length > 0 ) {
block = paragraphs.shift();
while ( !block.getParent().equals( commonParent ) )
block = block.getParent();
if ( !block.equals( lastBlock ) )
tmp.push( block );
lastBlock = block;
}
// If any of the selected blocks is a blockquote, remove it to prevent
// nested blockquotes.
while ( tmp.length > 0 ) {
block = tmp.shift();
if ( block.getName() == 'blockquote' ) {
var docFrag = new CKEDITOR.dom.documentFragment( editor.document );
while ( block.getFirst() ) {
docFrag.append( block.getFirst().remove() );
paragraphs.push( docFrag.getLast() );
}
docFrag.replace( block );
} else {
paragraphs.push( block );
}
}
// Now we have all the blocks to be included in a new blockquote node.
var bqBlock = editor.document.createElement( 'blockquote' );
bqBlock.insertBefore( paragraphs[ 0 ] );
while ( paragraphs.length > 0 ) {
block = paragraphs.shift();
bqBlock.append( block );
}
} else if ( state == CKEDITOR.TRISTATE_ON ) {
var moveOutNodes = [],
database = {};
while ( ( block = iterator.getNextParagraph() ) ) {
var bqParent = null,
bqChild = null;
while ( block.getParent() ) {
if ( block.getParent().getName() == 'blockquote' ) {
bqParent = block.getParent();
bqChild = block;
break;
}
block = block.getParent();
}
// Remember the blocks that were recorded down in the moveOutNodes array
// to prevent duplicates.
if ( bqParent && bqChild && !bqChild.getCustomData( 'blockquote_moveout' ) ) {
moveOutNodes.push( bqChild );
CKEDITOR.dom.element.setMarker( database, bqChild, 'blockquote_moveout', true );
}
}
CKEDITOR.dom.element.clearAllMarkers( database );
var movedNodes = [],
processedBlockquoteBlocks = [];
database = {};
while ( moveOutNodes.length > 0 ) {
var node = moveOutNodes.shift();
bqBlock = node.getParent();
// If the node is located at the beginning or the end, just take it out
// without splitting. Otherwise, split the blockquote node and move the
// paragraph in between the two blockquote nodes.
if ( !node.getPrevious() )
node.remove().insertBefore( bqBlock );
else if ( !node.getNext() )
node.remove().insertAfter( bqBlock );
else {
node.breakParent( node.getParent() );
processedBlockquoteBlocks.push( node.getNext() );
}
// Remember the blockquote node so we can clear it later (if it becomes empty).
if ( !bqBlock.getCustomData( 'blockquote_processed' ) ) {
processedBlockquoteBlocks.push( bqBlock );
CKEDITOR.dom.element.setMarker( database, bqBlock, 'blockquote_processed', true );
}
movedNodes.push( node );
}
CKEDITOR.dom.element.clearAllMarkers( database );
// Clear blockquote nodes that have become empty.
for ( i = processedBlockquoteBlocks.length - 1; i >= 0; i-- ) {
bqBlock = processedBlockquoteBlocks[ i ];
if ( noBlockLeft( bqBlock ) )
bqBlock.remove();
}
if ( editor.config.enterMode == CKEDITOR.ENTER_BR ) {
var firstTime = true;
while ( movedNodes.length ) {
node = movedNodes.shift();
if ( node.getName() == 'div' ) {
docFrag = new CKEDITOR.dom.documentFragment( editor.document );
var needBeginBr = firstTime && node.getPrevious() && !( node.getPrevious().type == CKEDITOR.NODE_ELEMENT && node.getPrevious().isBlockBoundary() );
if ( needBeginBr )
docFrag.append( editor.document.createElement( 'br' ) );
var needEndBr = node.getNext() && !( node.getNext().type == CKEDITOR.NODE_ELEMENT && node.getNext().isBlockBoundary() );
while ( node.getFirst() )
node.getFirst().remove().appendTo( docFrag );
if ( needEndBr )
docFrag.append( editor.document.createElement( 'br' ) );
docFrag.replace( node );
firstTime = false;
}
}
}
}
selection.selectBookmarks( bookmarks );
editor.focus();
},
refresh: function( editor, path ) {
// Check if inside of blockquote.
var firstBlock = path.block || path.blockLimit;
this.setState( editor.elementPath( firstBlock ).contains( 'blockquote', 1 ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
},
context: 'blockquote',
allowedContent: 'blockquote',
requiredContent: 'blockquote'
};
CKEDITOR.plugins.add( 'blockquote', {
// jscs:disable maximumLineLength
lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
// jscs:enable maximumLineLength
icons: 'blockquote', // %REMOVE_LINE_CORE%
hidpi: true, // %REMOVE_LINE_CORE%
init: function( editor ) {
if ( editor.blockless )
return;
editor.addCommand( 'blockquote', commandObject );
editor.ui.addButton && editor.ui.addButton( 'Blockquote', {
label: editor.lang.blockquote.toolbar,
command: 'blockquote',
toolbar: 'blocks,10'
} );
}
} );
} )();
i am quite new to Javafx, and would like your help in solving the below:
I have two tableviews, and i would like to compare the column values between the two and highlight the cell of one of the tables, if the values are different.
(Assumption - both tables have same number of columns, restricting rows to the minimum content among tables)
ex:
table 1 has
column1 column2 column3
1 America Newyork
table 2 has
column1 column2 column3
1 America Washington
The second table last column should be highlighted with a textfill
I have this pseudo code, doesnt seem to work though
int colsize1= tableview1.getColumns().size();
int colsize2= tableview2.getColumns().size();
if(colsize1 != colsize2) {
alert("number of columns do not match, cannot be validated");
return;
}
ObservableList<ObservableList<String>> da1 = FXCollections.observableArrayList();
ObservableList<ObservableList<String>> da2 = FXCollections.observableArrayList();
da1.addAll(tableview1.getItems());
da2.addAll(tableview2.getItems());
int size = Math.min(da1.size(), da2.size());
for(int i = 0; i < size; i++) {
for (int j = 0; j < colsize1; j++) {
if(!(tableview1.getItems().get(i).get(j).equals(tableview2.getItems().get(i).get(j)))) {
String value = tableview1.getItems().get(i).get(j);
String value2 = tableview2.getItems().get(i).get(j);
int currentrow = i;
TableColumn<TableView<ObservableList<String>>, String> columns = (TableColumn) tableview2.getColumns().get(j);
columns.setCellFactory(column -> {
return new TableCell<TableView<ObservableList<String>>, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
System.out.println("item is " + item + " empty is " + empty);
if(empty){
if(value != value2){
this.setStyle("-fx-background-color : yellow");
}
}
if (!empty) {
if((value != item) && currentrow == getTableRow().getIndex()) {
// int rowindex = getTableRow().getIndex();
System.out.println(" item is " + item);
this.setText(item);
this.setTextFill(Color.RED);
this.setStyle("-fx-background-color : yellow");
}else {
this.setText(item);
}
}
}
};
});
}
}
}
}
I found a solution to the given problem.
#FXML
public void onvalidatebutton() {
int colsize1 = tableview1.getColumns().size();
int colsize2 = tableview2.getColumns().size();
if (colsize1 != colsize2) {
alert("number of columns do not match, cannot be validated");
return;
}
int size = tableview1.getItems().size();
for (int i = 0; i < size; i++) {
for (int j = 0; j < colsize1; j++) {
TableColumn columns2 = tableview2.getColumns().get(j);
// String value = tableview1.getItems().get(currentrow).get(j);
settingcell(columns2);
}
}
}
private void settingcell(TableColumn<ObservableList<String>,String> column) {
column.setCellFactory(col -> {
return new TableCell<ObservableList<String>, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
int ind = getTableView().getVisibleLeafIndex(column);
// System.out.println(" ind is " + ind);
String value = tableview1.getItems().get(this.getIndex()).get(ind);
// System.out.println(" value is " + value + " & item is "+ item);
if (!(StringUtils.equals(value,item))) {
this.setText(item);
this.setTextFill(Color.RED);
this.setStyle("-fx-background-color : yellow");
} else {
this.setText(item);
}
}
else{
this.setText("");
}
}
};
});
}